MySQL Guru eller ej, problemet er, at medmindre du finder en måde at filtrere forskellige rækker fra, skal afstanden beregnes mellem hvert punkt og hver by...
Der er to generelle tilgange, der kan hjælpe situationen
- gør afstandsformlen enklere
- filtrer usandsynlige kandidater fra til radius på 100.000 fra en given by
Inden du går ind på disse to forbedringsmuligheder, bør du beslutte dig for det ønskede præcisionsniveau med hensyn til denne 100 miles distance, og du bør også angive, hvilket geografisk område der er dækket af databasen (er dette blot det kontinentale USA osv.
Grunden til dette er, at selvom den er mere præcis numerisk, er Great Circle-formlen meget beregningsmæssigt dyr. En anden mulighed for forbedring af ydeevnen ville være at gemme "Grid-koordinater" af slagsen ud over (eller i stedet for) Lat/Long-koordinaterne.
Rediger :
Et par ideer om en enklere (men mindre præcis) formel :
Da vi har at gøre med relativt små afstande (og jeg gætter på mellem 30 og 48 grader lat nord), kan vi bruge den euklidiske afstand (eller endnu bedre kvadratet på den euklidiske afstand) i stedet for mere komplicerede formler for sfærisk trigonometri.
afhængigt af det forventede præcisionsniveau kan det endda være acceptabelt at have én enkelt parameter for den lineære afstand for en fuld længdegrad, idet man tager noget gennemsnit over det betragtede areal (f.eks. ca. 46 vedtægt miles). Formlen bliver så
LatDegInMi = 69.0
LongDegInMi = 46.0
DistSquared = ((Lat1 - Lat2) * LatDegInMi) ^2 + ((Long1 - Long2) * LongDegInMi) ^2
På ideen om en kolonne med gitterinfo til at filtrere for at begrænse antallet af rækker tages i betragtning til afstandsberegning.
Hvert "punkt" i systemet, hvad enten det er en by eller et andet punkt (?leveringssteder, butikssteder... uanset hvad) er tildelt to heltalskoordinater, som definerer kvadratet på f.eks. 25 miles * 25 miles, hvor punktet ligger. Koordinaterne for ethvert punkt inden for 100 miles fra referencepunktet (en given by), vil højst være +/- 4 i x-retningen og +/- 4 i y-retningen. Vi kan derefter skrive en forespørgsel, der ligner følgende
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;
Bemærk, at LongDegInMi enten kan være hårdkodet (samme for alle steder på det kontinentale USA) eller komme fra tilsvarende post i postnummertabellen. På samme måde kunne LatDegInMi være hårdkodet (lidt behov for at få det til at variere, da det i modsætning til den anden er relativt konstant).
Grunden til at dette er hurtigere er, at for de fleste poster i det kartesiske produkt mellem postnummertabellen og pointtabellen, beregner vi slet ikke afstanden. Vi eliminerer dem på basis af en indeksværdi (GridX og GridY).
Dette bringer os til spørgsmålet om, hvilke SQL-indekser vi skal producere. Helt sikkert vil vi måske have:- GridX + GridY + Status (på pointtabellen)- GridY + GridX + status (muligvis)- By + Stat + breddegrad + længdegrad + GridX + GridY på postnummertabellen
Et alternativ til gitteret er at "afgrænse" grænserne for bredde- og længdegrad, som vi vil overveje, baseret på bredde- og længdegraden for en given by. dvs. JOIN-betingelsen bliver et interval i stedet for et 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))