Obecnie mam prawie milion lokalizacji w bazie danych mysql, wszystkie z informacjami o długości i szerokości geograficznej.
Próbuję znaleźć odległość między jednym punktem a wieloma innymi punktami za pomocą zapytania. To nie jest tak szybkie, jak chcę, szczególnie przy ponad 100 trafieniach na sekundę.
Czy istnieje szybsze zapytanie lub być może szybszy system inny niż mysql? Korzystam z tego zapytania:
SELECT
name,
( 3959 * acos( cos( radians(42.290763) ) * cos( radians( locations.lat ) )
* cos( radians(locations.lng) - radians(-71.35368)) + sin(radians(42.290763))
* sin( radians(locations.lat)))) AS distance
FROM locations
WHERE active = 1
HAVING distance < 10
ORDER BY distance;
Uwaga: podana odległość jest w milach . Jeśli potrzebujesz Kilometrów , użyj 6371
zamiast 3959
.
Odpowiedzi:
Utwórz punkty, używając
Point
wartościGeometry
typów danych wMyISAM
tabeli. Od wersji Mysql 5.7.5InnoDB
tabele obsługują teraz takżeSPATIAL
indeksy.Utwórz
SPATIAL
indeks tych punktówUżyj,
MBRContains()
aby znaleźć wartości:lub, w
MySQL 5.1
i powyżej:Spowoduje to zaznaczenie wszystkich punktów w przybliżeniu w ramce
(@lat +/- 10 km, @lon +/- 10km)
.W rzeczywistości nie jest to pudełko, lecz sferyczny prostokąt: segment kuli związany z szerokością i długością geograficzną. To może różnić się od prostokąta na Ziemi Franciszka Józefa , ale dość blisko w większości zamieszkałych miejsc.
Zastosuj dodatkowe filtrowanie, aby zaznaczyć wszystko wewnątrz okręgu (nie kwadrat)
Ewentualnie zastosuj dodatkowe dokładne filtrowanie w celu uwzględnienia odległości dużego koła (dla dużych odległości)
źródło
@lon - 10 / ( 111.1 / cos(@lat))
(i będzie drugą w parze, gdy wszystko będzie poprawne.cos(lon)
jest dokładne tylko w przypadku niewielkich odległości. Zobacz janmatuschek.de/LatitudeLongitudeBoundingCoordinates111.(1)
km szerokości geograficznej.mypoint
to pole w tabeli, w którym przechowywane są współrzędne.Nie jest to odpowiedź specyficzna dla MySql, ale poprawi wydajność twojej instrukcji SQL.
To, co skutecznie robisz, to obliczanie odległości do każdego punktu w tabeli, aby sprawdzić, czy jest w odległości 10 jednostek od danego punktu.
Co możesz zrobić przed uruchomieniem tej sql, to utworzyć cztery punkty, które narysują pudełko 20 jednostek na boku, z punktem w środku, tj. (x1, y1). . . (x4, y4), gdzie (x1, y1) to (dane długie + 10 jednostek, dane Lat + 10 jednostek). . . (danyLong - 10 jednostek, danyLat -10 jednostek). Właściwie potrzebujesz tylko dwóch punktów, nazwij je lewym górnym i prawym dolnym (X1, Y1) i (X2, Y2)
Teraz twoja instrukcja SQL używa tych punktów, aby wykluczyć wiersze, które zdecydowanie przekraczają 10u od twojego punktu, może używać indeksów szerokości i długości geograficznej, więc będzie o rząd wielkości szybszy niż to, co obecnie masz.
na przykład
Podejście pudełkowe może zwrócić fałszywie dodatnie (możesz zbierać punkty w rogach pudełka, które są> 10u od danego punktu), więc nadal musisz obliczyć odległość każdego punktu. Jednak to znowu będzie znacznie szybsze, ponieważ drastycznie ograniczyłeś liczbę punktów do przetestowania do punktów w ramce.
Nazywam tę technikę „Myśleniem w pudełku” :)
EDYCJA: Czy można to umieścić w jednej instrukcji SQL?
Przepraszam, nie mam pojęcia, do czego zdolny jest mój SQL lub Php. Nie wiem, gdzie najlepiej jest zbudować cztery punkty, ani jak można je przekazać do zapytania mySql w Php. Jednak gdy zdobędziesz cztery punkty, nic nie stoi na przeszkodzie, aby połączyć własną instrukcję SQL z moją.
Wiem, że dzięki MS SQL mogę zbudować instrukcję SQL, która deklaruje cztery zmiennoprzecinkowe (X1, Y1, X2, Y2) i oblicza je przed „główną” instrukcją select, tak jak powiedziałem, nie mam pojęcia, czy można to zrobić za pomocą MySql. Jednak nadal będę skłonny zbudować cztery punkty w języku C # i przekazać je jako parametry do zapytania SQL.
Przykro mi, że nie mogę pomóc, jeśli ktoś może odpowiedzieć na części MySQL i Php tego, edytuj tę odpowiedź, aby to zrobić.
źródło
Poniższa funkcja MySQL została opublikowana na tym blogu . Nie testowałem tego zbyt wiele, ale z tego, co zebrałem z postu, jeśli twoje pola szerokości i długości geograficznej są indeksowane , może to działać dobrze:
Przykładowe użycie:
Zakładając, że tabela jest wywoływana
places
z polamilatitude
ilongitude
:źródło
SELECT ROUND(((ACOS(SIN(lat1 * PI() / 180) * SIN(lat2 * PI() / 180) + COS(lat1 * PI() / 180) * COS(lat2 * PI() / 180) * COS((lnt1 - lnt2) * PI() / 180)) * 180 / PI()) * 60 * 1.1515) * 1.609344 * 1000) AS distance
Musiałem rozwiązać podobny problem (filtrowanie wierszy według odległości od pojedynczego punktu) i łącząc oryginalne pytanie z odpowiedziami i komentarzami, opracowałem rozwiązanie, które doskonale działa dla mnie zarówno w MySQL 5.6, jak i 5.7.
coordinates
jest polem typuPOINT
i maSPATIAL
indeks6371
służy do obliczania odległości w kilometrach56.946285
szerokość geograficzna dla punktu centralnego24.105078
długość geograficzna dla punktu centralnego15
to maksymalna odległość w kilometrachW moich testach MySQL używa indeksu SPATIAL na
coordinates
polu, aby szybko wybrać wszystkie wiersze znajdujące się w prostokącie, a następnie oblicza rzeczywistą odległość dla wszystkich filtrowanych miejsc, aby wykluczyć miejsca z narożników prostokątów i pozostawić tylko miejsca wewnątrz okręgu.Oto wizualizacja mojego wyniku:
Szare gwiazdy wizualizują wszystkie punkty na mapie, żółte gwiazdy zwracają zapytanie MySQL. Szare gwiazdy w rogach prostokąta (ale poza okręgiem) zostały wybrane przez,
MBRContains()
a następnie cofnięte przezHAVING
klauzulę.źródło
jeśli używasz MySQL 5.7. *, możesz użyć st_distance_sphere (POINT, POINT) .
źródło
Jest to zapytanie o obliczenie odległości między punktami w MySQL, użyłem go w długiej bazie danych, działa idealnie! Uwaga: wykonaj zmiany (nazwa bazy danych, nazwa tabeli, kolumna itp.) Zgodnie z własnymi wymaganiami.
źródło
źródło
źródło
źródło
Funkcja MySQL, która zwraca liczbę metrów między dwiema współrzędnymi:
Aby zwrócić wartość w innym formacie, zamień funkcję
6371000
in na promień Ziemi w wybranej jednostce. Na przykład byłyby kilometry6371
i mile3959
.Aby użyć tej funkcji, po prostu wywołaj ją tak, jak każdą inną funkcję w MySQL. Na przykład, jeśli masz stolik
city
, możesz znaleźć odległość między każdym miastem do każdego innego miasta:źródło
Pełny kod ze szczegółowymi informacjami na temat instalacji jako wtyczki MySQL znajduje się tutaj: https://github.com/lucasepe/lib_mysqludf_haversine
Zamieściłem ten komentarz w zeszłym roku. Ponieważ uprzejmie @TylerCollier zaproponował mi opublikowanie jako odpowiedzi, oto jest.
Innym sposobem jest napisanie niestandardowej funkcji UDF, która zwraca odległość w poziomie od dwóch punktów. Ta funkcja może pobierać dane wejściowe:
Możemy więc napisać coś takiego:
aby pobrać wszystkie rekordy z odległości mniejszej niż 40 kilometrów. Lub:
aby pobrać wszystkie rekordy z odległości mniejszej niż 25 stóp.
Podstawową funkcją jest:
źródło
Szybkiej, prostej i dokładnej (dla mniejszych odległości) aproksymacji można dokonać za pomocą projekcji sferycznej . Przynajmniej w moim algorytmie routingu otrzymuję wzrost o 20% w porównaniu do prawidłowego obliczenia. W kodzie Java wygląda to tak:
Nie jestem pewien co do MySQL (przepraszam!).
Upewnij się, że wiesz o ograniczeniu (trzeci parametr assertEquals oznacza dokładność w kilometrach):
źródło
Oto bardzo szczegółowy opis Geo Distance Search z MySQL, rozwiązaniem opartym na implementacji Haversine Formula na mysql. Pełny opis rozwiązania z teorią, implementacją i dalszą optymalizacją wydajności. Chociaż część optymalizacji przestrzennej nie działała poprawnie w moim przypadku. http://www.scribd.com/doc/2569355/Geo-Distance-Search-w--MySQL
źródło
Zapoznaj się z Geo Distance Search z MySQL , rozwiązaniem opartym na implementacji Haversine Formula na MySQL. Jest to pełny opis rozwiązania z teorią, implementacją i dalszą optymalizacją wydajności. Chociaż część optymalizacji przestrzennej nie działała poprawnie w moim przypadku.
Zauważyłem w tym dwa błędy:
użycie
abs
w instrukcji select na p8. Właśnie pominąłemabs
i zadziałało.funkcja odległości poszukiwania przestrzennego na p27 nie przekształca się w radiany ani nie mnoży długości geograficznej
cos(latitude)
, chyba że jego dane przestrzenne zostaną w tym uwzględnione (nie można tego stwierdzić z kontekstu artykułu), ale jego przykład na p26 wskazuje, że jego dane przestrzennePOINT
nie są załadowane radiany lub stopnie.źródło
źródło
Korzystanie z mysql
Widzieć: https://andrew.hedges.name/experiments/haversine/
Widzieć: https://stackoverflow.com/a/24372831/5155484
Widzieć: http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
UWAGA:
LEAST
służy do unikania wartości zerowych jako komentarza sugerowanego na https://stackoverflow.com/a/24372831/5155484źródło