MySQL Guru czy nie, problem polega na tym, że jeśli nie znajdziesz sposobu na odfiltrowanie różnych wierszy, odległość między każdym punktem a każdym miastem musi zostać obliczona...
Istnieją dwa ogólne podejścia, które mogą pomóc w tej sytuacji
- uprość formułę odległości
- odfiltruj mało prawdopodobnych kandydatów w promieniu 100 tys. z danego miasta
Zanim przejdziesz do tych dwóch ścieżek poprawy, powinieneś zdecydować o pożądanym poziomie precyzji w odniesieniu do tej odległości 100 mil, a także wskazać, który obszar geograficzny obejmuje baza danych (czy to tylko kontynentalne USA itp.
Powodem tego jest to, że formuła Wielkiego Koła, choć bardziej precyzyjna pod względem liczbowym, jest bardzo kosztowna obliczeniowo. Inną drogą poprawy wydajności byłoby przechowywanie „współrzędnych siatki” jako dodatek (lub zamiast) współrzędnych szerokości/długości.
Edytuj :
Kilka pomysłów na prostszą (ale mniej precyzyjną) formułę :
Ponieważ mamy do czynienia ze stosunkowo małymi odległościami (zgaduję, że między 30 a 48 stopni szerokości geograficznej północnej), możemy użyć odległości euklidesowej (lub jeszcze lepiej kwadratu odległości euklidesowej) zamiast bardziej skomplikowane formuły trygonometrii sferycznej.
W zależności od oczekiwanego poziomu precyzji, może być nawet dopuszczalne posiadanie jednego parametru dla odległości liniowej dla pełnego stopnia długości geograficznej, przyjmując średnią wartość dla rozważanego obszaru (powiedzmy około 46 statut mil). Formuła stałaby się wtedy
LatDegInMi = 69.0
LongDegInMi = 46.0
DistSquared = ((Lat1 - Lat2) * LatDegInMi) ^2 + ((Long1 - Long2) * LongDegInMi) ^2
O pomyśle kolumn z informacjami o siatce do filtrowania w celu ograniczenia liczby wierszy brane pod uwagę przy obliczaniu odległości.
Każdemu "punktowi" w systemie, czy to miastu, czy innym punktom (?lokalizacje dostaw, lokalizacje sklepów... cokolwiek) są przypisane dwie współrzędne całkowite, które definiują kwadrat, powiedzmy 25 mil * 25 mil, gdzie leży punkt. Współrzędne dowolnego punktu w promieniu 100 mil od punktu odniesienia (danego miasta) będą wynosić co najwyżej +/-4 w kierunku x i +/-4 w kierunku y. Możemy wtedy napisać zapytanie podobne do następującego
SELECT city, state, latitude, longitude, COUNT(*)
FROM zipcodes Z
JOIN points P
ON P.GridX IN (
SELECT GridX - 4, GridX - 3, GridX - 2, GridX - 1, GridX, GridX +1, GridX + 2 GridX + 3, GridX +4
FROM zipcode ZX WHERE Z.id = ZX.id)
AND
P.GridY IN (
SELECT GridY - 4, GridY - 3, GridY - 2, GridY - 1, GridY, GridY +1, GridY + 2 GridY + 3, GridY +4
FROM zipcode ZY WHERE Z.id = ZY.id)
WHERE P.Status = A
AND ((Z.latitude - P.latitude) * LatDegInMi) ^2
+ ((Z.longitude - P.longitude) * LongDegInMi) ^2 < (100^2)
GROUP BY city,state,latitude,longitude;
Zauważ, że LongDegInMi może być albo zakodowany na stałe (tak samo dla wszystkich lokalizacji w kontynentalnych Stanach Zjednoczonych), albo pochodzić z odpowiedniego rekordu w tabeli kodów pocztowych. Podobnie LatDegInMi może być zakodowany na sztywno (niewiele trzeba go zmieniać, ponieważ w przeciwieństwie do innych jest stosunkowo stały).
Powodem, dla którego jest to szybsze, jest to, że w przypadku większości rekordów w iloczynie kartezjańskim między tabelą kodów pocztowych a tabelą punktów w ogóle nie obliczamy odległości. Eliminujemy je na podstawie wartości indeksu (GridX i GridY).
To prowadzi nas do pytania, które indeksy SQL należy tworzyć. Na pewno możemy chcieć:- GridX + GridY + Status (w tabeli punktów)- GridY + GridX + status (ewentualnie)- Miasto + Stan + szerokość geograficzna + długość geograficzna + GridX + GridY w tabeli kodów pocztowych
Alternatywą dla siatek jest „powiązanie” granic szerokości i długości geograficznej, które rozważymy na podstawie szerokości i długości geograficznej danego miasta. tzn. warunek JOIN staje się zakresem, a nie IN :
JOIN points P
ON P.latitude > (Z.Latitude - (100 / LatDegInMi))
AND P.latitude < (Z.Latitude + (100 / LatDegInMi))
AND P.longitude > (Z.longitude - (100 / LongDegInMi))
AND P.longitude < (Z.longitude + (100 / LongDegInMi))