Desværre ST_Distance() < threshold
er ikke en sargable
søgekriteriet. For at tilfredsstille denne forespørgsel skal MySQL beregne funktionens værdi for hver række i tabellen og derefter sammenligne den med tærsklen. Så den skal lave en fuld tabelscanning (eller måske en fuld indeksscanning).
For at udnytte et indeks til at fremskynde denne forespørgsel, skal du bruge et afgrænsningsbokskriterium. Forespørgslen er meget mere omfattende, men også meget hurtigere. Hvis du antager, at dine x/y-punkter i din geometri repræsenterer breddegrad/længdegrad i grader, kan den forespørgsel se sådan ud:
set @latpoint = 38.0234332;
set @lngpoint = -94.0724223;
set @r = 10.0; /* ten mile radius */
set @units=69.0; /* 69 statute miles per degree */
SELECT AsText(geo)
FROM markers
WHERE MbrContains(GeomFromText(
CONCAT('LINESTRING(', @latpoint-(@r/@units),' ',
@lngpoint-(@r /(@units* COS(RADIANS(@latpoint)))),
',',
@latpoint+(@r/@units) ,' ',
@lngpoint+(@r /(@units * COS(RADIANS(@latpoint)))),
')')),
geo)
Hvordan virker det? For det første er MbrContains( bundet, vare) funktion er sargable . For en anden ting giver den store grimme konkat genstand en diagonal linje fra sydvest til det nordøstlige hjørne af det afgrænsende rektangel. Ved at bruge dit datapunkt og ti mile radius ser det sådan ud.
LINESTRING(37.8785 -94.2564,38.1684 -93.8884)
Når du bruger GeomFromText()
gengivelse af den diagonale linje i det første argument til MbrContains()
det tjener som et afgrænsende rektangel. MbrContains()
kan derefter udnytte det smarte quadtree-geometriindeks.
For det tredje, ST_Distance()
, i MySQL, håndterer ikke store cirkelbredde- og længdegradsberegninger. (PostgreSQL
har en mere omfattende GIS-udvidelse
.) MySQL'er er lige så dum som en flapjack i flatland. Det antager, at dine punkter i dine geometriske objekter er repræsenteret i plan geometri. Så ST_Distance() < 10.0
med lng/lat-punkter gør noget mærkeligt.
Der er én fejl i de resultater, denne forespørgsel genererer; det returnerer alle punkterne i afgrænsningsrammen, ikke kun inden for den angivne radius. Det kan løses med en separat afstandsberegning. Jeg har skrevet alt dette i nogle detaljer her .
Bemærk :For GPS-opløsning bredde- og længdegrad, 32-bit FLOAT
data har tilstrækkelig præcision. DOUBLE
er hvad MySQL's geo-udvidelse bruger. Når du arbejder i grader, er mere end fem steder efter decimaltegnet ud over GPS-præcisionen. DECIMAL()
er ikke en ideel datatype for lat/lng-koordinater.