MySQL Guru nebo ne, problém je v tom, že pokud nenajdete způsob, jak odfiltrovat různé řádky, je třeba vypočítat vzdálenost mezi každým bodem a každým městem...
Existují dva obecné přístupy, které mohou situaci pomoci
- zjednodušte vzorec vzdálenosti
- odfiltrujte nepravděpodobné kandidáty do okruhu 100 000 z daného města
Než se pustíte do těchto dvou cest vylepšení, měli byste se rozhodnout o požadované úrovni přesnosti s ohledem na tuto vzdálenost 100 mil a také byste měli uvést, která geografická oblast je pokryta databází (je to jen kontinentální USA atd.
Důvodem je to, že i když je vzorec Velkého kruhu přesnější numericky, je velmi výpočetně nákladný. Další cestou ke zlepšení výkonu by bylo uložení "souřadnic mřížky" druhů vedle (nebo místo nich) souřadnic Lat/Long.
Upravit :
Několik nápadů na jednodušší (ale méně přesný) vzorec :
Vzhledem k tomu, že máme co do činění s relativně malými vzdálenostmi (a hádám mezi 30 a 48 stupni severní šířky), můžeme použít euklidovskou vzdálenost (nebo ještě lépe druhou mocninu euklidovské vzdálenosti) spíše než složitější vzorce sférické trigonometrie.
v závislosti na očekávané úrovni přesnosti může být dokonce přijatelné mít jeden jediný parametr pro lineární vzdálenost pro celý stupeň zeměpisné délky, přičemž se vezme něco průměrného v uvažované oblasti (řekněme přibližně 46 statut míle). Vzorec by se pak stal
LatDegInMi = 69.0
LongDegInMi = 46.0
DistSquared = ((Lat1 - Lat2) * LatDegInMi) ^2 + ((Long1 - Long2) * LongDegInMi) ^2
Myšlenka sloupců s informacemi o mřížce k filtrování, aby se omezil počet řádků uvažováno pro výpočet vzdálenosti.
Každému "bodu" v systému, ať už je to město, nebo jiný bod (?místa dodání, umístění prodejen... cokoli), jsou přiřazeny dvě celočíselné souřadnice, které definují čtverec řekněme 25 mil * 25 mil, kde leží bod. Souřadnice jakéhokoli bodu do 100 mil od referenčního bodu (daného města) budou maximálně +/- 4 ve směru x a +/- 4 ve směru y. Poté můžeme napsat dotaz podobný následujícímu
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;
Všimněte si, že LongDegInMi může být buď pevně zakódováno (stejné pro všechna místa v rámci kontinentálních USA), nebo pocházet z odpovídajícího záznamu v tabulce PSČ. Podobně lze napevno zakódovat LatDegInMi (není potřeba jej měnit, protože na rozdíl od druhého je relativně konstantní).
Důvodem, proč je to rychlejší, je to, že u většiny záznamů v kartézském součinu mezi tabulkou PSČ a tabulkou bodů vůbec nepočítáme vzdálenost. Eliminujeme je na základě hodnoty indexu (GridX a GridY).
To nás přivádí k otázce, které indexy SQL vyrábět. Určitě můžeme chtít:- GridX + GridY + Stav (v tabulce bodů)- GridY + GridX + stav (možná)- Město + Stát + zeměpisná šířka + zeměpisná délka + GridX + GridY v tabulce PSČ
Alternativou k mřížkám je „svázat“ hranice zeměpisné šířky a délky, které budeme uvažovat na základě zeměpisné šířky a délky daného města. tj. podmínka JOIN se stává rozsahem spíše než 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))