I want to search nearest locations from coordinates (lat/lng) into mysql database, I use spherical law of cosines to searching these places :
SELECT onlycoord.id, locations.name, locations.ascii,
locations.latitude, locations.longitude,
locations.country, locations.elevation,
locations.gtopo30, locations.timezone,
(6371 * ACOS(
COS( RADIANS( 48.48 ) ) *
COS( RADIANS( onlycoord.latitude ) ) *
COS(
RADIANS( onlycoord.longitude ) -
RADIANS( 2.20 )
) +
SIN( RADIANS( 48.48 ) ) *
SIN( RADIANS( onlycoord.latitude ) )
)) AS distance
FROM onlycoord USE INDEX (coordinate)
LEFT JOIN locations USE INDEX (id)
ON (onlycoord.id = locations.id)
WHERE onlycoord.latitude BETWEEN (
48.48 - ( 5 / 111 )
) AND (
48.48 + ( 5 / 111 )
) AND onlycoord.longitude BETWEEN (
2.20 - ( 5 / ABS( COS(
RADIANS( 48.48 )
) * 111 ) )
) AND (
2.20 + ( 5 / ABS( COS(
RADIANS( 48.48 )
) * 111 ) )
)
ORDER BY distance ASC
LIMIT 0, 20
Where 6371 is earth radius (km), 111 (km) is 1° of latitude, cos(latitude) * 111 (km) is 1° of longitude and 5 (km) is search radius.
Problem : I want a minimum of 8 cities found, but 5 kms radius is small but fast for condensed zone (many cities), so if I use a search radius too big for condensed zone, query is slow (many results : order by), but for non-condensed the search radius is too small to find at least 8 cities...
How can I make a recursive query which increases automatically the search radius (x2) if the count of found cities < 8 (using mysql only) ?
Thanks
MySQL doesn't support recursive queries.
You have to make new queries depending on the result of the previous query.
By the way, for spatial queries I'd recommend a spatial database.
Related
I have the following query which does indeed work. It takes in a hard set Latitude and Longitude and finds the closest set of coordinates, along with it's postal code.
What I'm having trouble with is the speed of the query. It's currently taking 3.8 seconds to run and I have 9000 separate coordinates to check.
Any advice on how to speed this up would be greatly appreciated.
SELECT pcds, ROUND(MIN(distance), 4) AS distance FROM
(SELECT `postcode`.`pcds`,(
6371 * acos (
cos ( radians('51.4932392') )
* cos( radians( `postcode`.`lat` ) )
* cos( radians( `postcode`.`lng` ) - radians('-0.0846429') )
+ sin ( radians('51.4932392') )
* sin( radians( `postcode`.`lat` ) )
)
) AS distance
FROM postcode
ORDER BY distance
LIMIT 1
) AS First
I am trying to move our site over from using a Lat/Lng field (CHAR), when doing distance. This is how we currently do it:
SELECT ID,( 6371 * acos( cos( radians(52.35462) ) * cos( radians( glinks_Links.Latitude ) ) * cos( radians( glinks_Links.Longitude ) - radians(4.88227) ) + sin( radians(52.35462) ) * sin( radians( glinks_Links.Latitude ) ) ) ) AS distance FROM glinks_Links
WHERE
(
((Latitude BETWEEN (52.35462 - 40/69.0) AND (52.35462 + 40/69.0)) )
AND
(Longitude BETWEEN (4.88227 - 40/42.5) AND (4.88227 + 40/42.5))
)
HAVING distance < 40 ORDER BY distance
...and this comes back as
Showing rows 0 - 24 (1855 total, Query took 0.0288 seconds.)
Then another version that actually gives the "distance" back (as we want to sort by that, and only include the closest);
SELECT ID,st_distance_sphere(POINT(4.88227,52.35462), point_test) / 1000 AS distance FROM glinks_Links WHERE ( ((Latitude BETWEEN (52.35462 - 40/69.0) AND (52.35462 + 40/69.0)) ) AND (Longitude BETWEEN (4.88227 - 40/42.5) AND (4.88227 + 40/42.5)) ) HAVING DISTANCE < 100 ORDER BY distance LIMIT 100
Gives:
Showing rows 0 - 99 (100 total, Query took 0.0237 seconds.)
Then what seems to be the best:
SELECT *
FROM glinks_Links
where st_distance_sphere(POINT(4.88227,52.35462), point_test)/1000 <= 100
Showing rows 0 - 24 (3439 total, Query took 0.0015 seconds.)
The issue with that though - is that it doesn't provide me with the distance! What I want to do is query and grab the 100 closest. Is there a way I can do this, without compramising on the speed?
be sure you have proper composite index on
create index my_idx ON glinks_Links (Latitude, Longitude, ID)
And you don't need unuseful () for where condition (using AND)
SELECT ID,( 6371 * acos( cos( radians(52.35462) ) * cos( radians( glinks_Links.Latitude ) )
* cos( radians( glinks_Links.Longitude ) - radians(4.88227) ) + sin( radians(52.35462) )
* sin( radians( glinks_Links.Latitude ) ) ) ) AS distance
FROM glinks_Links
WHERE Latitude BETWEEN (52.35462 - 40/69.0) AND (52.35462 + 40/69.0)
AND Longitude BETWEEN (4.88227 - 40/42.5) AND (4.88227 + 40/42.5)
HAVING distance < 40
ORDER BY distance
for the second version
SELECT ID, st_distance_sphere(POINT(4.88227,52.35462), point_test) / 1000 AS distance
FROM glinks_Links
WHERE Latitude BETWEEN (52.35462 - 40/69.0) AND (52.35462 + 40/69.0)
AND Longitude BETWEEN (4.88227 - 40/42.5) AND (4.88227 + 40/42.5)
HAVING DISTANCE < 100
ORDER BY distance
LIMIT 100
you should use a composite index on
create index myidx ON glinks_Links (Latitude, Longitude, point_test, ID )
I have stores records in my table and I want to sort them on the basis of highest rating of store and which has nearest distance to my location.
SELECT rating,
( 3959 * acos( cos( radians(37) )
* cos( radians( lat ) )
* cos( radians( lon )
- radians(-122) )
+ sin( radians(37) )
* sin( radians( lat ) )
)
) AS distance
FROM mystores sr
order by sr.rating desc ,distance asc
It is not giving me my desired results
Table Mystores
id|rating|distance
66 5 55
55 4 56
99 3 60
I assume that you want just the closest store and you want to ignore all other with the same rank but with bigger distance.
The group by raiting allows us to get the minimal distance for each raiting.
select m1.*
from mystores m1
join (
select m.raiting,
min(m.distance) distance
from mystores m
group by m.raiting
) m2
on m2.raiting = m1.raiting and
m2.distance = m1.distance
order by m1.raiting desc, m1.distance asc
i want to search near by location with given latitude and longitude in mysql
latitude is : 26.902
longitude is : 75.793
and distance is : 30
Query is :
SELECT
id, (
3959 * acos (
cos ( radians(26.902) )
* cos( radians( latitude ) )
* cos( radians( longitude ) - radians(75.793) )
+ sin ( radians(26.902) )
* sin( radians( latitude ) )
)
) AS distance
FROM business
HAVING distance < 30
ORDER BY distance
LIMIT 0 , 20;
Result:
i am getting a record with distance is 3.58,
record lat/long are 26.89 / 75.74
but when i check online on other site i got distance 5.759 miles .
Some code available here as a Stored Function: http://mysql.rjweb.org/doc.php/latlng .
I have this mysql table:
CREATE TABLE markers (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
lat FLOAT( 10, 6 ) NOT NULL ,
lng FLOAT( 10, 6 ) NOT NULL
) ENGINE = MYISAM ;
with following data:
lat lng id
37.0010 -122.0010 1
37.0020 -122.0020 2
37.1010 -122.1010 3
37.1020 -122.1020 4
37.1030 -122.1030 5
37.2010 -122.2010 6
38.9000 -123.9000 7
38.9010 -123.9010 8
I know how to get the nearest locations:
SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20;
from http://code.google.com/intl/en/apis/maps/articles/phpsqlsearch.html
But how to cluster those nearest locations if there are more points inside a specific distance?
what i want is this result:
newlat newlng count_points
37.1020 -122.1020 3
37.0015 -122.0015 2
37.2010 -122.2010 1
Any input is greatly appreciated. Thanks
SELECT
id,
( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance,
COUNT(*) as count_points
FROM markers
GROUP BY newlat, newlng
HAVING distance < 25
ORDER BY distance ASC, count_points DESC
LIMIT 0 , 20;
You can use a quadkey or a geohash to cluster spatial data. A quadkey is often use to subdivide the map but you can also use it to cluster points of interest. There are many ways to compute a quadkey or geohash. The simplest is a morton curve.