It might be the repeated question but need help.
i am new to MySQL.
Here is my problem.
i have a query to calculate distance between latitude and longitude. based on the distance order i have to return id's.
SELECT dlo.id,
(3959 * acos(cos(radians(12.9)) * cos(radians(y(gproperty))) * cos(radians(x(gproperty)) - radians(77.5)) +sin(radians(12.9)) * sin(radians(y(gproperty))))) AS distance
FROM db1.gfeature dgf,
db2.loc dlo,
db2.cust dcu
WHERE gf.o_type = 6
AND dcu.id = 240
AND dgf.o_id = dlo.p_id HAVING distance < 20
ORDER BY distance LIMIT 10;
which returns as
+------+-----------------------+
| id | distance |
+------+-----------------------+
| 101 | 0.00025714756425665 |
| 199 | 0.10971525612556807 |
| 722 | 0.22772618588406165 |
+------+-----------------------+
But i need only id column to be displayed. I asked same-question yesterday. But now i am using three tables to get data. So confused in joining 3 tables.
Can anybody suggest me.?
I tried this way
select id from (
select
dlo.id,
( 3959 * acos( cos( radians(12.9) )
* cos( radians( y(gproperty) ) )
* cos( radians( x(gproperty) ) - radians(77.5) )
+ sin( radians(12.9) )
* sin( radians(y(gproperty) ) )
)
) AS distance
from db1.gfeature dgf
join db2.cust dcu, db2.loc dlo
on dgf.o_type = 6 and dcu.id = 10 and dgf.o_id = dlo.w_id
) t
where distance < 10
order by distance
limit 10;
but getting syntax error near "on..."
Maybe something like this. Just use subquery to achieve.
SELECT S.ID
FROM
(SELECT dlo.id,
(3959 * acos(cos(radians(12.9)) * cos(radians(y(gproperty))) * cos(radians(x(gproperty)) - radians(77.5)) +sin(radians(12.9)) * sin(radians(y(gproperty))))) AS distance
FROM db1.gfeature dgf, db2.loc dlo , db2.cust dcu
WHERE gf.o_type = 6 AND dcu.id = 240 AND dgf.o_id = dlo.p_id
HAVING distance < 20) S
ORDER BY S.distance
LIMIT 10;
Related
I have the following query in mysql
SELECT id,
(SELECT ( 6371 * Acos(Cos(Radians('31.502537032937294')) *
Cos(Radians(lat)) *
Cos(
Radians(lng) - Radians(
'74.34226725250483'))
+
Sin(
Radians('31.502537032937294')) *
Sin(Radians(lat))) ) AS distance
FROM `addresses`
WHERE orders.id = addresses.order_id
AND `type` = 'from'
AND `address_for` = 'order'
AND `is_archive` = 0
HAVING `distance` <= 50) AS distance
FROM orders
WHERE `is_archive` = 0
AND `is_complete` = 0
AND `status_id` = 1
AND Date(`expected_pick_up_at`) = '2018-09-10'
In which, I am calculating distance of orders from my current position. The result of this query is as following
id distance
1027 3.392817926161619
1028 2.957290773676651
While the actual distance is
id distance
1027 0.09565605656865
1028 0.43565656101560
original lat/long saved in the database from which I am calculating distance are as
id lat long
1027 31.53189017798653 74.35203921049833
1028 31.52842855342999 74.34939790517092
Can anyone guide me which part I am doing wrong in this,
Note: I have also looked at the following question
I'm not quite sure how to expand this query so that I can also have a AVG(price_current) column ... instead of having to calculate this value within PHP once the query comes back.
SELECT
listing_subtype, bedrooms, total_baths, tot_sqft_finished, price_current, latitude, longitude, (
3959 * acos (
cos ( radians(48.639) )
* cos( radians( latitude ) )
* cos( radians( longitude ) - radians(-123.404) )
+ sin ( radians(48.639) )
* sin( radians( latitude ) )
)
) AS distance
FROM rets_property_resi
WHERE listing_subtype = 'Single Family Detached' AND
bedrooms >= 2 AND bedrooms <= 3 AND
total_baths >= 1 AND total_baths <= 2 AND
tot_sqft_finished >= 2000 AND tot_sqft_finished <= 2500
HAVING distance < 5
ORDER BY distance
LIMIT 0, 25;
I'm going to make some assumptions here:
You look for similar houses/homes in a given radius around a requested one. Each of those homes has a primary key in the table you are querying from. Let us call that primKey here.
Additionally, you want the average price of all returned homes which is limited to 0,25, and not the AVG of all records in the table that match you WHERE clause.
All returned rows will have therefore an extra field with the average price and is the same for each row.
You will have to run the same query again as a subquery. However, since you want to limit the AVG you have to run that in a subquery to calculate the correct AVG.
This is really clunky:
SELECT
rets_property_resi.listing_subtype, rets_property_resi.bedrooms, rets_property_resi.total_baths, rets_property_resi.tot_sqft_finished, rets_property_resi.price_current, rets_property_resi.latitude, rets_property_resi.longitude, (
3959 * acos (
cos ( radians(48.639) )
* cos( radians( latitude ) )
* cos( radians( longitude ) - radians(-123.404) )
+ sin ( radians(48.639) )
* sin( radians( latitude ) )
)
) AS distance, outerSubQuery.averagePrice
FROM rets_property_resi
LEFT JOIN
(
SELECT innerSubQuery.primKey AS primKey, AVG(innerSubQuery.price_current) AS averagePrice
FROM
(SELECT
primKey, price_current, (
3959 * acos (
cos ( radians(48.639) )
* cos( radians( latitude ) )
* cos( radians( longitude ) - radians(-123.404) )
+ sin ( radians(48.639) )
* sin( radians( latitude ) )
)
) AS distance
FROM rets_property_resi
WHERE listing_subtype = 'Single Family Detached' AND
bedrooms >= 2 AND bedrooms <= 3 AND
total_baths >= 1 AND total_baths <= 2 AND
tot_sqft_finished >= 2000 AND tot_sqft_finished <= 2500
HAVING distance < 5
ORDER BY distance
LIMIT 0, 25) AS innerSubQuery
GROUP BY innerSubQuery.primKey
) AS outerSubQuery ON (outerSubQuery.primKey = rets_property_resi.primKey)
WHERE listing_subtype = 'Single Family Detached' AND
bedrooms >= 2 AND bedrooms <= 3 AND
total_baths >= 1 AND total_baths <= 2 AND
tot_sqft_finished >= 2000 AND tot_sqft_finished <= 2500
HAVING distance < 5
ORDER BY distance
LIMIT 0, 25;
I bet there is a more elegant version of this though. You might be better off with a temporary table here or keep just calculating in code.
I have defined a function that calculates the nearest item using the haversine formula. This function works correctly when I execute the query in an ad-hoc fashion such as:
SELECT CONCAT(address, ", ", deadline, ", NC") AS addr FROM tblTickets WHERE ticket = 'A152012363' LIMIT 1 INTO #Address;
SELECT lat, lng FROM AddressGeocode WHERE address = #Address LIMIT 1 INTO #LAT, #LNG;
SELECT ROUND(( 3959 * acos( cos( radians(#LAT) ) * cos( radians( startlat ) ) * cos( radians( startlon ) - radians(#LNG) ) + sin( radians(#LAT) ) * sin( radians( startlat ) ) ) ) * 5280.0 ) AS distance
FROM tblAsBuiltPolys
ORDER BY distance
LIMIT 1 INTO #RET;
SELECT #RET
When I place the logic in a function so that I can re use it, the result is always the same until I start another session.
I initially thought the issue was that I was using session variables so I adjusted the function to be this:
CREATE FUNCTION `getTicketBuffer`(`ticket` VARCHAR(50))
RETURNS DOUBLE
LANGUAGE SQL
NOT DETERMINISTIC
READS SQL DATA
COMMENT ''
BEGIN
DECLARE ADDR VARCHAR(250) DEFAULT NULL;
DECLARE LT DOUBLE DEFAULT NULL;
DECLARE LG DOUBLE DEFAULT NULL;
DECLARE RET DOUBLE DEFAULT NULL;
SET ADDR = (SELECT CONCAT(address, ", ", deadline, ", NC") AS addr FROM tblTickets WHERE ticket = ticket LIMIT 1);
SELECT lat, lng INTO LT, LG FROM AddressGeocode WHERE address = ADDRESS LIMIT 1;
SET RET = (SELECT ROUND(( 3959 * acos( cos( radians(LT) ) * cos( radians( startlat ) ) * cos( radians( startlon ) - radians(LG) ) + sin( radians(LT) ) * sin( radians( startlat ) ) ) ) * 5280.0 ) AS distance
FROM tblAsBuiltPolys
ORDER BY distance
LIMIT 1);
RETURN RET;
END
I am pulling my hair out trying to make this work so that the following query will execute correctly.
SELECT ticket, getTicketBuffer(ticket) AS d
FROM tblTickets WHERE ticket IN
("A152012363","C152011366","A152012358","C152011309","A152012353","A152011315");
When I exectute the logic directly in a query I get the following:
mysql> SELECT ROUND(( 3959 * acos( cos( radians(#LAT) ) * cos( radians( startlat ) ) * cos( radians( startlon ) - radians(#LNG) ) + sin( radians(#LAT) ) * sin( radians( startlat ) ) ) ) * 5280.0 ) AS distance
-> FROM tblAsBuiltPolys
-> ORDER BY distance
-> LIMIT 1 INTO #RET;
Query OK, 1 row affected (0.02 sec)
mysql> SELECT #RET
-> ;
+------+
| #RET |
+------+
| 130 |
+------+
1 row in set (0.00 sec)
mysql>
mysql> SELECT ticket, getTicketBuffer(ticket) AS d FROM tblTickets WHERE ticket IN("A152012363","C152011366","A152012358","C152011309","A152012353","A152011315");
+------------+------+
| ticket | d |
+------------+------+
| A152011315 | 81 |
| A152012353 | 81 |
| A152012358 | 81 |
| A152012363 | 81 |
| C152011309 | 81 |
| C152011366 | 81 |
+------------+------+
6 rows in set (0.12 sec)
I am trying to get the "TicketBuffer" for each ticket in a list of tickets, if this can be made into a view, then that would work as well.
Does anyone see any obvious mistakes? I am not an expert at SQL, particularly stored functions/procedures so I am sure this can be done a better way.
Tables :-
Table 1 :- Person_LatLong
Person_Id
Lat
Lng
Location_DateTime
Person_LatLong Data :-
10 23.030211421184454 72.55705489668806 2014-09-02 04:23:42
10 23.03029215373424 72.55710855670746 2014-09-02 04:25:09
10 23.030301667271765 72.55715279051745 2014-09-02 04:27:21
11 19.045563510415214 72.915665750359 2014-09-02 08:22:07
11 19.046567530190785 72.91524330185979 2014-09-02 08:32:36
11 19.04553070382594 72.91621148133476 2014-09-02 08:39:47
11 18.537267778519347 73.83538450865574 2014-09-03 01:44:19
11 18.33554237666039 73.85274219500492 2014-09-03 07:18:02
11 18.331919816746026 73.8525499279805 2014-09-03 07:18:59
11 18.33181875247372 73.85243149060277 2014-09-03 07:19:02
11 18.777939290860722 73.31834934181029 2014-09-04 22:07:31
11 18.790032969638293 73.30265963437363 2014-09-04 22:09:06
11 18.79108238318203 73.29928216416553 2014-09-04 22:09:22
11 18.800857529132163 73.28531940244517 2014-09-04 22:11:22
11 18.812675453346255 73.27794458217039 2014-09-04 22:13:26
11 18.82985965773455 73.25592224937081 2014-09-04 22:15:31
11 18.84531169311457 73.23344887176076 2014-09-04 22:17:49
11 18.869063931831764 73.2185512231118 2014-09-04 22:19:54
11 18.893204517796047 73.20479873759578 2014-09-04 22:22:03
11 18.910161939581506 73.18348844819505 2014-09-04 22:24:30
Table 2:- LS_For
Subject_Id -> This is basically Person_Id but I have changed the name.
Watcher_Id
Assistance_Group_Id
LS_For Table Data :-
1 10 1
1 11 1
1 17 1
Query:-
SELECT *,( 6371 * acos( cos( radians(23.030) )
* cos( radians(Lat) ) * cos( radians(Lng) - radians(72.5570) )
+ sin( radians(23.030) ) * sin( radians(Lat) ) ) )
AS distance FROM Person_LatLong WHERE Person_Id IN
(SELECT Watcher_Id FROM LS_For WHERE Subject_Id = 1 AND Assistance_Group_Id = 1)
HAVING distance < 5 ORDER BY Location_DateTime DESC;
Gives Below as Output :- ID | LAT | LNG | DATE TIME | DISTANCE
10 23.030301667271765 72.55715279051745 2014-09-02 04:27:21 0.037008818510632306
10 23.03029215373424 72.55710855670746 2014-09-02 04:25:09 0.03433299317128307
10 23.030211421184454 72.55705489668806 2014-09-02 04:23:42 0.02417068347133403
11 23.030301667271765 72.55715279051745 2014-09-02 05:27:21 0.037008818510632306
11 23.03029215373424 72.55710855670746 2014-09-02 05:25:09 0.03433299317128307
11 23.030211421184454 72.55705489668806 2014-09-02 05:23:42 0.02417068347133403
After Adding GROUP BY :-
SELECT *,( 6371 * acos( cos( radians(23.030) )
* cos( radians(Lat) ) * cos( radians(Lng) - radians(72.5570) )
+ sin( radians(23.030) ) * sin( radians(Lat) ) ) )
AS distance FROM Person_LatLong WHERE Person_Id IN
(SELECT Watcher_Id FROM LS_For WHERE Subject_Id = 1 AND Assistance_Group_Id = 1)
GROUP BY Person_Id HAVING distance < 5 ORDER BY Location_DateTime DESC;
Gives me below Output :-
10 23.030211421184454 72.55705489668806 2014-09-02 04:23:42 0.02417068347133403
But I want to get latest time row not the oldest.
OUTPUT Required :- Working with 2 tables, LS_For is table where I am finding the Ids for which I have to look for the location distance and based on that need to filter the result based on distance.
10 23.030301667271765 72.55715279051745 2014-09-02 04:27:21 0.037008818510632306
11 23.030301667271765 72.55715279051745 2014-09-02 05:27:21 0.037008818510632306
Generate a set of data containing the max date time (most recent) for each person and then INNER JOIN to it so that your base set islimited to only the most recent entry in person_LatLong.
(Select person_ID, max(`Location_DateTime`) mldt FROM person_LatLong group by Person_ID)
generates the most recent latLong for each user... So then...
POSSIBLE 1
SELECT a.Person_ID, a.LAT, a.LNG, a.`Location_DateTime`,
( 6371 * acos( cos( radians(23.030) )
* cos( radians(a.Lat) ) * cos( radians(a.Lng) - radians(72.5570) )
+ sin( radians(23.030) ) * sin( radians(a.Lat) ) ) ) AS Distance
FROM Person_LatLong a
INNER JOIN (SELECT Person_ID, max(`Location_DateTime`) as mldt
FROM Person_latLong
GROUP BY Person_ID) P
on P.Person_ID = a.Person_Id
and P.mldt = a.`Location_DateTime`
LEFT JOIN LS_FOR C
on a.Person_Id = C.Watcher_ID
AND C.Subject_Id = 1
AND C.Assistance_Group_Id = 1
GROUP BY a.Person_ID, a.LAT, a.LNG, a.`Location_DateTime`
HAVING Distance < 5;
POSSIBLE 2
SELECT a.Person_ID, a.LAT, a.LNG, a.`Location_DateTime`,
( 6371 * acos( cos( radians(23.030) )
* cos( radians(a.Lat) ) * cos( radians(a.Lng) - radians(72.5570) )
+ sin( radians(23.030) ) * sin( radians(a.Lat) ) ) ) AS Distance
FROM Person_LatLong a
INNER JOIN (SELECT Person_ID, max(`Location_DateTime`) as mldt
FROM Person_latLong
GROUP BY Person_ID) P
on P.Person_ID = a.Person_Id
and P.mldt = a.`Location_DateTime`
INNER JOIN LS_FOR C
on a.Person_Id = C.Watcher_ID
WHERE C.Subject_Id = 1
AND C.Assistance_Group_Id = 1
GROUP BY a.Person_ID, a.LAT, a.LNG, a.`Location_DateTime`
HAVING Distance < 5;
I've amend the response to treat LS_FOR as a left join incase there are not records for each user in LS_FOR, and if not still return the person even though they wouldn't have a subject_ID or assistance_groupID....
In other words, The above should return in plain english:
The most recent personLatLong will be returned for each unique Person_ID; provided the distance is < 5 and if there is an entry in LS_FOR and that PersonLatLong entry, only records with a subjectID of 1 and asstianceGorupID of 1 will be returned. If there is no entry in LS_FOR it will still return the person (Perhaps you do want it as an inner join...)
Instead of
GROUP BY Person_I
Use
LIMIT 1
OR
GROUP BY Person_Id HAVING distance < 5 ORDER BY Location_DateTime DESC;
to
GROUP BY Person_Id HAVING distance < 5 ORDER BY Location_DateTime ASC;
Check this link.
Retrieving the last record in each group
Here the solution is much faster than sub query.
select person1.* from Person_LatLong person1
LEFT JOIN Person_LatLong person2 on person1.id = person2.id and
person1.dateTiem < person2.dateTime
where person2.id IS NULL;
This will give you latest record.
try doing this:
SELECT
*,( 6371 * acos( cos( radians(23.030) ) * cos( radians(Lat) ) * cos( radians(Lng) - radians(72.5570) ) + sin( radians(23.030) ) * sin( radians(Lat) ) ) ) AS distance
FROM
(select * from Person_LatLong ORDER BY Location_DateTime DESC) as t
WHERE
Person_Id IN (SELECT Watcher_Id FROM LS_For WHERE Subject_Id = 1 AND Assistance_Group_Id = 1)
GROUP BY
Person_Id
HAVING
distance < 5 ;
Hi I have the following table
--------------------------------------------
| id | city | Latitude | Longitude |
--------------------------------------------
| 1 | 3 | 34.44444 | 84.3434 |
--------------------------------------------
| 2 | 4 | 42.4666667 | 1.4666667 |
--------------------------------------------
| 3 | 5 | 32.534167 | 66.078056 |
--------------------------------------------
| 4 | 6 | 36.948889 | 66.328611 |
--------------------------------------------
| 5 | 7 | 35.088056 | 69.046389 |
--------------------------------------------
| 6 | 8 | 36.083056 | 69.0525 |
--------------------------------------------
| 7 | 9 | 31.015833 | 61.860278 |
--------------------------------------------
Now I want to get distance between two points. Say a user is having a city 3 and a user is having a city 7. My scenario is one user having a city and latitue and longtitude is searching other users distance from his city. For example user having city 3 is searching. He wants to get distance of user of any other city say it is 7. I have searched and found following query
SELECT `locations`.`city`, ( 3959 * acos ( cos ( radians(31.589167) ) * cos( radians( Latitude ) ) * cos( radians( Longitude ) - radians(64.363333) ) + sin ( radians(31.589167) ) * sin( radians( Latitude ) ) ) ) AS `distance` FROM `locations` HAVING (distance < 50)
As for as I know this query finds distance from one point to all other points. Now I want to get distance from one point to other point.
Any guide line will be much appreciated.
I think your question says you have the city values for the two cities between which you wish to compute the distance.
This query will do the job for you, yielding the distance in km. It uses the spherical cosine law formula.
Notice that you join the table to itself so you can retrieve two coordinate pairs for the computation.
SELECT a.city AS from_city, b.city AS to_city,
111.111 *
DEGREES(ACOS(LEAST(1.0, COS(RADIANS(a.Latitude))
* COS(RADIANS(b.Latitude))
* COS(RADIANS(a.Longitude - b.Longitude))
+ SIN(RADIANS(a.Latitude))
* SIN(RADIANS(b.Latitude))))) AS distance_in_km
FROM city AS a
JOIN city AS b ON a.id <> b.id
WHERE a.city = 3 AND b.city = 7
Notice that the constant 111.1111 is the number of kilometres per degree of latitude, based on the old Napoleonic definition of the metre as one ten-thousandth of the distance from the equator to the pole. That definition is close enough for location-finder work.
If you want statute miles instead of kilometres, use 69.0 instead.
http://sqlfiddle.com/#!9/21e06/412/0
If you're looking for nearby points you may be tempted to use a clause something like this:
HAVING distance_in_km < 10.0 /* slow ! */
ORDER BY distance_in_km DESC
That is (as we say near Boston MA USA) wicked slow.
In that case you need to use a bounding box computation. See this writeup about how to do that. http://www.plumislandmedia.net/mysql/haversine-mysql-nearest-loc/
The formula contains a LEAST() function. Why? Because the ACOS() function throws an error if its argument is even slightly greater than 1. When the two points in question are very close together, the expression with the COS() and SIN() computations can sometimes yield a value slightly greater than 1 due to floating-point epsilon (inaccuracy). The LEAST(1.0, dirty-great-expression) call copes with that problem.
There's a better way, a formula by Thaddeus Vincenty. It uses ATAN2() rather than ACOS() so it's less susceptible to epsilon problems.
Edit 2022 (by Alexio Vay):
As of today the modern solution should be the following short code:
select ST_Distance_Sphere(
point(-87.6770458, 41.9631174),
point(-73.9898293, 40.7628267))
Please check out the answer of Naresh Kumar.
You can use the ST_Distance_Sphere() MySQL built-in function, supported since MySQL 5.7 version and above.
It computes the distance in meters more efficiently.
select ST_Distance_Sphere(point(lng, lat), point(lng,lat))
i.e.
select ST_Distance_Sphere(
point(-87.6770458, 41.9631174),
point(-73.9898293, 40.7628267)
)
Referred from Calculating distance using MySQL
Heres is MySQL query and function which use to get distance between two latitude and longitude and distance will return in KM.
Mysql Query :-
SELECT (6371 * acos(
cos( radians(lat2) )
* cos( radians( lat1 ) )
* cos( radians( lng1 ) - radians(lng2) )
+ sin( radians(lat2) )
* sin( radians( lat1 ) )
) ) as distance
FROM your_table;
Mysql Function :-
DELIMITER $$
CREATE FUNCTION `getDistance`(`lat1` VARCHAR(200), `lng1` VARCHAR(200), `lat2` VARCHAR(200), `lng2` VARCHAR(200)) RETURNS varchar(10) CHARSET utf8
begin
declare distance varchar(10);
set distance = (select (6371 * acos(
cos( radians(lat2) )
* cos( radians( lat1 ) )
* cos( radians( lng1 ) - radians(lng2) )
+ sin( radians(lat2) )
* sin( radians( lat1 ) )
) ) as distance);
if(distance is null)
then
return '';
else
return distance;
end if;
end$$
DELIMITER ;
How to use in your PHP Code
SELECT getDistance($lat1,$lng1,$lat2,$lng2) as distance
FROM your_table.
Here's a MySQL function that will take two latitude/longitude pairs, and give you the distance in degrees between the two points. It uses the Haversine formula to calculate the distance. Since the Earth is not a perfect sphere, there is some error near the poles and the equator.
To convert to miles, multiply by 3961.
To convert to kilometers, multiply by 6373.
To convert to meters, multiply by 6373000.
To convert to feet, multiply by (3961 * 5280) 20914080.
DELIMITER $$
CREATE FUNCTION \`haversine\`(
lat1 FLOAT, lon1 FLOAT,
lat2 FLOAT, lon2 FLOAT
) RETURNS float
NO SQL
DETERMINISTIC
COMMENT 'Returns the distance in degrees on the Earth between two known points of latitude and longitude. To get miles, multiply by 3961, and km by 6373'
BEGIN
RETURN DEGREES(ACOS(
COS(RADIANS(lat1)) *
COS(RADIANS(lat2)) *
COS(RADIANS(lon2) - RADIANS(lon1)) +
SIN(RADIANS(lat1)) * SIN(RADIANS(lat2))
));
END;
DELIMITER;
Not sure how your distance calculation is going on but you need to do a self join your table and perform the calculation accordingly. Something like this probably
select t1.id as userfrom,
t2.id as userto,
( 3959 * acos ( cos ( radians(31.589167) ) * cos( radians( t1.Latitude ) ) *
cos( radians( t1.Longitude ) - radians(64.363333) ) + sin ( radians(31.589167) ) *
sin( radians( t2.Latitude ) ) ) ) AS `distance`
from table1 t1
inner join table1 t2 on t2.city > t1.city
IMPORTANT! Anyone using or copying these calculations MAKE SURE to use least(1.0, (...)) when passing the calculation to the acos() function. The acos() function will NOT take a value above 1 and I have found when comparing lat/lng values that are identical there are times when the calculations come out to something like 1.000002. This will produce a distance of NULL instead of 0 and may not return results you're looking for depending on how your query is structured!
This is CORRECT:
select round(
( 3959 * acos( least(1.0,
cos( radians(28.4597) )
* cos( radians(lat) )
* cos( radians(lng) - radians(77.0282) )
+ sin( radians(28.4597) )
* sin( radians(lat)
) ) )
), 1) as distance
from locations having distance <= 60 order by distance
This is WRONG:
select round(
( 3959 * acos(
cos( radians(28.4597) )
* cos( radians(lat) )
* cos( radians(lng) - radians(77.0282) )
+ sin( radians(28.4597) )
* sin( radians(lat)
) )
), 1) as distance
from locations having distance <= 60 order by distance
The highest rated answer also talks about this, but I wanted to make sure this was very clear since I just found a long standing bug in my query.
Here's a formula I converted from https://www.geodatasource.com/developers/javascript
It's a nice clean function that calculates the distance in KM
DELIMITER $$
CREATE DEFINER=`root`#`localhost` FUNCTION `FN_GET_DISTANCE`(
lat1 DOUBLE, lng1 DOUBLE, lat2 DOUBLE, lng2 DOUBLE
) RETURNS double
BEGIN
DECLARE radlat1 DOUBLE;
DECLARE radlat2 DOUBLE;
DECLARE theta DOUBLE;
DECLARE radtheta DOUBLE;
DECLARE dist DOUBLE;
SET radlat1 = PI() * lat1 / 180;
SET radlat2 = PI() * lat2 / 180;
SET theta = lng1 - lng2;
SET radtheta = PI() * theta / 180;
SET dist = sin(radlat1) * sin(radlat2) + cos(radlat1) * cos(radlat2) * cos(radtheta);
SET dist = acos(dist);
SET dist = dist * 180 / PI();
SET dist = dist * 60 * 1.1515;
SET dist = dist * 1.609344;
RETURN dist;
END$$
DELIMITER ;
You'll also find the same function in different languages on the site;
Maybe someone will come in handy, I managed to implement my task through the FN_GET_DISTANCE function:
SELECT SUM (t.distance) as Distance FROM
(SELECT (CASE WHEN (FN_GET_DISTANCE (Latitude, Longitude, #OLDLatitude, #OLDLongitude)) BETWEEN 0.01 AND 2 THEN
FN_GET_DISTANCE (Latitude, Longitude, #OLDLatitude, #OLDLongitude) ELSE 0 END) AS distance,
IF (#OLDLatitude IS NOT NULL, #OLDLatitude: = Latitude, 0),
IF (#OLDLongitude IS NOT NULL, #OLDLongitude: = Longitude, 0)
FROM `data`, (SELECT #OLDLatitude: = 0) var0, (SELECT #OLDLongitude: = 0) var1
WHERE ID_Dev = 1
AND DateTime BETWEEN '2021-05-23 08:00:00' AND '2021-05-23 20:00:00'
ORDER BY ID DESC) t;