MySQL Guru eller inte, problemet är att om du inte hittar ett sätt att filtrera bort olika rader, måste avståndet beräknas mellan varje punkt och varje stad...
Det finns två allmänna tillvägagångssätt som kan hjälpa situationen
- gör avståndsformeln enklare
- filtrera bort osannolika kandidater till radien på 100 000 från en given stad
Innan du går in på dessa två förbättringsvägar bör du bestämma dig för vilken precisionsnivå som önskas med hänsyn till denna 100 mils sträcka, och du bör också ange vilket geografiskt område som täcks av databasen (är detta bara kontinentala USA etc.
Anledningen till detta är att även om den är mer exakt numeriskt, är Great Circle-formeln mycket beräkningsmässigt dyr. En annan väg för prestandaförbättring skulle vara att lagra "Grid-koordinater" av olika slag utöver (eller istället för) Lat/Long-koordinaterna.
Redigera :
Några idéer om en enklare (men mindre exakt) formel :
Eftersom vi har att göra med relativt små avstånd, (och jag gissar på mellan 30 och 48 grader Lat North), kan vi använda det euklidiska avståndet (eller ännu bättre kvadraten på det euklidiska avståndet) snarare än mer komplicerade formler för sfärisk trigonometri.
beroende på den förväntade precisionsnivån kan det till och med vara acceptabelt att ha en enda parameter för det linjära avståndet för en full longitud, som tar något medelvärde över den yta som betraktas (säg cirka 46 stadga miles). Formeln skulle då bli
LatDegInMi = 69.0
LongDegInMi = 46.0
DistSquared = ((Lat1 - Lat2) * LatDegInMi) ^2 + ((Long1 - Long2) * LongDegInMi) ^2
På idén om en kolumn med rutnätsinformation att filtrera för att begränsa antalet rader beaktas för avståndsberäkning.
Varje "punkt" i systemet, oavsett om det är en stad eller en annan punkt (?leveransplatser, butiksplatser... vad som helst) tilldelas två heltalskoordinater som definierar kvadraten på säg 25 miles * 25 miles där punkten ligger. Koordinaterna för en punkt inom 100 miles från referenspunkten (en given stad), kommer som mest att vara +/- 4 i x-riktningen och +/- 4 i y-riktningen. Vi kan sedan skriva en fråga som liknar följande
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;
Observera att LongDegInMi antingen kan vara hårdkodad (samma för alla platser inom kontinentala USA), eller komma från motsvarande post i postnummertabellen. På liknande sätt kan LatDegInMi vara hårdkodad (lite behov av att få det att variera, eftersom det till skillnad från den andra är relativt konstant).
Anledningen till att detta är snabbare är att för de flesta poster i den kartesiska produkten mellan postnummertabellen och poängtabellen så beräknar vi inte avståndet alls. Vi eliminerar dem på basis av ett indexvärde (GridX och GridY).
Detta för oss till frågan om vilka SQL-index som ska produceras. Visst, vi kanske vill ha:- GridX + GridY + Status (på poängtabellen)- GridY + GridX + status (eventuellt)- Stad + State + latitud + longitud + GridX + GridY på postnummertabellen
Ett alternativ till rutnäten är att "binda" gränserna för latitud och longitud som vi kommer att överväga, baserat på latitud och longitud för en given stad. dvs JOIN-villkoret blir ett intervall snarare än ett 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))