I have a table called place
:
id | name | coordinates (longitude, latitude)
1 | London | -0.12574, 51.50853
2 | Manchester | -2.25, 53.41667
3 | Glasgow | -4.25, 55.86667
The coordinates
column is of the point datatype. I inserted the points into the place
table using:
st_geomfromtext('point($longitude $latitude)', 4326)
Notice that I've made use of the SRID.
Given any coordinates, I'd like to find the nearest places to it (order by ascending). The solution I have currently come up with (by reading the MySQL docs) looks like this:
select
*,
st_distance_sphere(`place`.`coordinates`, st_geomfromtext('Point($longitude $latitude)', 4326)) as distance
from place
order by distance asc;
After looking at countless similar questions on here and elsewhere, it's quite obvious that this is a lesser known (and newer way) of doing things so there's not much content on this, hence why I'm looking for a bit of clarification.
My questions are:
- Is this the best solution / Am I doing this right?
- Will this method make use of the spatial index I have on the
coordinates
column? - When using st_distance_sphere, do I need to specify the radius of the Earth in order to get accurate results? (Edit: no, it uses the radius of the earth by default)
Edit, here are those answers:
explain select ...;
returns:
id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra
1 | SIMPLE | place | NULL | ALL | NULL | NULL | NULL | NULL | 115687 | 100.00 | Using filesort
flush status; select ...; show session status like 'Handler%';
returns:
Variable_name | Value
Handler_commit | 1
Handler_delete | 0
Handler_discover | 0
Handler_external_lock | 2
Handler_mrr_init | 0
Handler_prepare | 0
Handler_read_first | 1
Handler_read_key | 1001
Handler_read_last | 0
Handler_read_next | 0
Handler_read_prev | 0
Handler_read_rnd | 1000
Handler_read_rnd_next | 119395
Handler_rollback | 0
Handler_savepoint | 0
Handler_savepoint_rollback | 0
Handler_update | 0
Handler_write | 0
It may be the best solution. Let's get some other answers first...
What does
EXPLAIN SELECT ...
say? (This may answer your Q2).Your query will scan the entire table, regardless of the other answers. Perhaps you want
LIMIT ...
on the end?Another thing that might be useful (depending on your app and on the Optimizer): Add a bounding box to the
WHERE
clause.In any case, do the following to get an accurate feel for how many rows are actually touched:
Get back with those answers; then perhaps we can iterate further.
After SHOW STATUS
Well,
Handler_read_rnd_next
says that it was a full table scan. The 1000 and 1001 -- did you haveLIMIT 1000
?I deduce that
LIMIT
is not factored into howSPATIAL
works. That is, it does the simplistic thing -- (1) check all rows, (2) sort, (3)LIMIT
.So, what to do?
Plan A: Decide that you don't want results farther than X miles (km) and add a "bounding box" to the query.
Plan B: Abandon Spatial and dig into a more complex way to do the task: http://mysql.rjweb.org/doc.php/latlng