sql query: cluster lat lng in specific area, order by points - mysql

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.

Related

Virtual column unknown when comparing lat lng

I am attempting to return only rows where the latitude and longitude being passed into the query, when compared to the latitude and longitude stored in the database, is a certain amount of miles apart.
The query is as follows:
SELECT
c.google_theatre_id
AS cinema_id,
c.name
AS cinema_name,
( 3959 * acos( cos( radians('50.4521013') ) *
cos( radians( latitude ) ) *
cos( radians( longitude ) -
radians('-3.5247389') ) +
sin( radians('50.4521013') ) *
sin( radians( latitude ) ) ) )
AS distance
FROM
google_cinemas c, app_users u
WHERE
distance < u.range
AND
u.id = 126
ORDER BY
distance
The query is designed to get the distance and then compare it to a column (range) in the app_users table.
When running the query, I'm getting an error of distance being an unknown column.
As this is a virtual column, is there a different way of comparing?
Thanks :)
you need to use HAVING instead of WHERE.. think of it this way WHERE is when you make an order at a restraunt and HAVING is picking stuff off of the plate when it comes to your table... you cannot reference an alias before the plate comes to your table only after it has been built
SELECT
c.google_theatre_id AS cinema_id,
c.name AS cinema_name,
( 3959 * acos( cos( radians('50.4521013') ) *
cos( radians( latitude ) ) *
cos( radians( longitude ) -
radians('-3.5247389') ) +
sin( radians('50.4521013') ) *
sin( radians( latitude ) ) )
) AS distance
FROM google_cinemas c, app_users u
WHERE u.id = 126
HAVING distance < u.range
ORDER BY distance
alternatively you can use it as a sub query which could be faster since HAVING re-evaluates the entire query.
SELECT *
FROM
( SELECT
c.google_theatre_id AS cinema_id,
c.name AS cinema_name,
( 3959 * acos( cos( radians('50.4521013') ) *
cos( radians( latitude ) ) *
cos( radians( longitude ) -
radians('-3.5247389') ) +
sin( radians('50.4521013') ) *
sin( radians( latitude ) ) )
) AS distance,
u.range
FROM google_cinemas c, app_users u
WHERE u.id = 126
ORDER BY distance
)t
WHERE distance < range

Combining multiple where conditions in mysql SELECT query

So here's my issue:
I have a database table where I have latitudes and longitudes and a timestamp. I need to be able to search through this table using PHP. What would the query be to find rows with lats and lons in a certain range, and, on top of this, in a certain time frame.
I have found two separate queries that would work while browsing through the internet, but I can't find a clear way to combine multiple conditions.
The two queries are:
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;
enter code here
SELECT * FROM `table` WHERE `date_field` BETWEEN 'date1' AND 'date2'
I need to find top twenty results where timestamp and lat and long are in range.
Thanks!
EDIT: All fields are in the same table.
If all data is in the same table, you can do:
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
WHERE date_field BETWEEN 'date1' AND 'date2'
HAVING distance < 25
ORDER BY distance
LIMIT 0 , 20;

combine 2 select queries in mysql

I have 2 select statements:
timestamp of emp getting awards for specific emp id
SELECT * FROM user_table,employeetable,awards where user_table.empid=employeetable.empid AND user_table.empid=awards.empid AND user_table.empid=123 ORDER BY timestamp DESC
All employees staying around 25 miles from the current loc:current location: lat =37 lng=-122
SELECT * ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) )+ sin( radians(37) ) * sin( radians( lat ) ) ) ) AS distance FROM user_table,employeetable,awards where user_table.empid=employeetable.empid AND user_table.empid=awards.empid HAVING distance < 25 ORDER BY distance;
How do I combine both and ORDER BY timestamp ?btw both have field timestamp.
1.has specific user
2.all users within specific radius
I really appreciate any help.Thanks in Advance.
You can combine the two queries into a single query, just using logic in the where clause (which this has turned into a having clause:
select *, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) * cos( radians( lng ) - radians(-122) )+ sin( radians(37) ) * sin( radians( lat ) ) ) ) as distance
from user u join
employee e
on u.empid = e.empid join
awards a
on u.empid = a.empid
having empid = 123 or distance < 25;
This uses having instead of where so the distance column alias can be used instead of the formula.

Two the same subqueries in query - how to make them 1?

I have table places
place_id | city | country_code | zipcode | lat | lon
Now I want to show places that are within 25 miles of place A. Place A has place_id 1.
SELECT *
FROM `places`
WHERE ( 3959 * acos( cos( radians((SELECT lat FROM places WHERE place_id=1)) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians((SELECT lon FROM places WHERE place_id=1)) ) + sin( radians((SELECT lat FROM places WHERE place_id=1)) ) * sin( radians( lat ) ) ) ) < 25;
This works ok, but there are two the same subqueries
SELECT lat FROM places WHERE place_id=1
Is it possible to optimize this query to not have two the same subqueries but make them into 1?
Your three subqueries will execute for every row in the outer table.
What you can do is convert those subqueries to a single JOIN which will only execute once for the entire query in order to find the latitude and longitude of place_id 1:
SELECT a.*
FROM places a
JOIN (SELECT lat, lon FROM places WHERE place_id = 1) b ON
(3959 * acos( cos( radians(b.lat) ) * cos( radians( a.lat ) ) * cos( radians( a.lon ) - radians(b.lon) ) + sin( radians(b.lat) ) * sin( radians( a.lat ) ) ) ) < 25;
Derive a new table from the subquery and make join with the original table, like
SELECT *
FROM places p, (SELECT lat AS new_lat FROM places WHERE place_id = 1) l
WHERE blah blah
now you can replace the subquery with column 'new_lat'.
You can use variable #var_lon to cache output of query as:
SELECT *
FROM `places`
WHERE ( 3959 * acos( cos( radians((SELECT lat FROM places WHERE place_id=1)) ) * cos( radians( lat ) ) * cos( radians( lon ) - radians((SELECT #var_lon := lon FROM places WHERE place_id=1)) ) + sin( radians(#var_lon) ) * sin( radians( lat ) ) ) ) < 25;

SQL location from a known point query

I have a MySQL database with the following table
int - id
float 2,6 - long
float 2,6 - lat
int - radius
I want to create a SQL query which returns the ID & Distance from a given location(long & lat)
I found the following piece of code which works:
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;
I want to alter this query to return only the rows where the computed length is smaller the radius (a column i my table)
replacing the 25 with the radius doesn't work.
Is there a way to achieve that without using two SQL queries ?
problem is occured because the result distance is in float and radius is an integer datatype so you need to CAST radius as float. try below
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 < CAST (radius AS float) ORDER BY distance LIMIT 0 , 20;
HAPPY TO HELP :)