mysql SELECT MIN from WHERE result - mysql

I have a table with several routes which has severeal points defined by lattitude and longitude.
table name: route_path
|id_route |id_point| lat | lng |
|hhVFlBFA0M| 328| 48.90008 | 18.0233 |
|hhVFlBFA0M| 329| 48.90003 | 18.0268 |
|hhVFlBFA0M| 330| 48.89997 | 18.02856 |
|hhVFlBFA0M| 331| 48.89991 | 18.02857 |
|hhVFlBFA0M| 332| 48.89986 | 18.02862 |
|hhVFlBFA0M| 333| 48.89982 | 18.02869 |
|hhVFlBFA0M| 334| 48.89981 | 18.02878 |
|hhVFlBFA0M| 335| 48.89981 | 18.02886 |
|hhVFlBFA0M| 336| 48.89956 | 18.02925 |
|hhVFlBFA0M| 337| 48.89914 | 18.02972 |
|hhVFlBFA0M| 338| 48.8986177 | 18.0302365|
|3toCyDGVV2| 1| 48.134166 | 17.1051961|
|3toCyDGVV2| 2| 48.13417 | 17.1052 |
|3toCyDGVV2| 3| 48.13344 | 17.10559 |
|3toCyDGVV2| 4| 48.13298 | 17.10609 |
|3toCyDGVV2| 5| 48.13221 | 17.10699 |
|3toCyDGVV2| 6| 48.132 | 17.10806 |
|3toCyDGVV2| 7| 48.13193 | 17.10997 |
|3toCyDGVV2| 8| 48.13203 | 17.1109 |
|3toCyDGVV2| 9| 48.132 | 17.1 112 |
|3toCyDGVV2| 10| 48.13181512| 17.1112 |
|3toCyDGVV2| 11| 48.13181 | 17.10806 |
|3toCyDGVV2| 12| 48.13181 | 17.10806 |
|3toCyDGVV2| 13| 48.13197 | 17.10399 |
|3toCyDGVV2| 14| 48.13199 | 17.10352 |
|3toCyDGVV2| 15| 48.1323 | 17.10328 |
So far I can do it to select all rows from one route which are within tolerated distance and then loop to find minimal distance point.
SELECT * FROM route_path
WHERE
(((lat < $start_lat + $tolerance) AND
(lat > $start_lat - $tolerance)) AND
((lng < $start_lng + $tolerance) AND
(lng > $start_lng - $tolerance)))
So this will results in several rows (id_points) of each route and then I need to loop with while to find minimal.
How can I found out select one row (one id_point) from each route with minimal distance from start lat and lng considering this distance is not more then some value.
Any suggestion for sql request without looping.
Basically I need something like, but of course it is not possible to use MIN after WHERE
SELECT * FROM route_path WHERE **MIN(**(((lat < $start_lat + $tolerance) AND (lat > $start_lat - $tolerance)) AND ((lng < $start_lng + $tolerance) AND (lng > $start_lng - $tolerance)))**)**

There are a few ways to calculate the distance between 2 points. The most efficient are probably using spatial data types which are designed for this and have indexes for this. I am not yet that experience with these so if you want to alter your database to use these I will just point you at this previous question to get the basics (the accepted answer covers it):-
Fastest Way to Find Distance Between Two Lat/Long Points
If you want to use your table as it currently stands then you can get the distance in km between 2 points with the following calculation:-
111.045 * DEGREES(ACOS(COS(RADIANS(lat_point_1))
* COS(RADIANS(lat_point_2))
* COS(RADIANS(long_point_1) - RADIANS(long_point_2))
+ SIN(RADIANS(lat_point_1))
* SIN(RADIANS(lat_point_2))))
(taken from here).
Using this if you wanted to know the closest point on a particular route to your starting point you could use this (no need to multiply by 111.045 unless you care about the actual distance rather than it just being the closest one):-
SELECT id_route,
id_point,
lat,
lng,
DEGREES(ACOS(COS(RADIANS($start_lat))
* COS(RADIANS(lat))
* COS(RADIANS($start_lng) - RADIANS(lng))
+ SIN(RADIANS($start_lat))
* SIN(RADIANS(lat)))) AS distance_in_km
FROM route_path
WHERE id_route = 'hhVFlBFA0M'
ORDER BY distance_in_km
LIMIT 1
If you wanted to know the closest point on EACH route to your starting point you would calculate the closest point on each route, then join that to your original table where the distance for that point matches the min distance (this will cause a problem if 2 points on a single route are exactly the same distance from your start point)
SELECT route_path.id_route,
route_path.id_point,
route_path.lat,
route_path.lng
FROM route_path
INNER JOIN
(
SELECT id_route,
MIN(DEGREES(ACOS(COS(RADIANS($start_lat))
* COS(RADIANS(lat))
* COS(RADIANS($start_lng) - RADIANS(lng))
+ SIN(RADIANS($start_lat))
* SIN(RADIANS(lat))))) AS distance_in_km
FROM route_path
GROUP BY id_route
) sub0
ON route_path.id_route = sub0.id_route
AND DEGREES(ACOS(COS(RADIANS($start_lat))
* COS(RADIANS(lat))
* COS(RADIANS($start_lng) - RADIANS(lng))
+ SIN(RADIANS($start_lat))
* SIN(RADIANS(lat)))) = sub0.distance_in_km

Related

PHP MySQL How to calculate distance for between row?

I have a table like this:
stepID | UserID| Date | Lat | Lng
1 |1 | 2019-10-11 | -7.2905838 | 112.5655568
2 |1 | 2019-10-11 | -7.2349607 | 112.6106177
3 |1 | 2019-10-11 | -7.2345435 | 112.6112432
4 |1 | 2019-10-12 | -7.2529265 | 112.6542999
I need to calculate distance that user has been visited on the same day (for example 2019-10-11). So waht will be show on PHP page is (the KM amount below is an example) :
From step 1 to 2: 2 KM
From step 2 to 3: 3 KM
From step 3 to 4: 3 KM
TOTAL FOR TODAY: 8 KM
I've googling and also search in this stackoverflow's history but didn't found like what I face today. Need your suggestion how to query this.
Thank you before, GBU always.
This is not the best task for MySQL. It would be much better to perform it in any programming language by reading rows from DB 1 by 1.
However if you want to use exactly MySQL then something like that (assuming StepIDs are sequential numbers without gaps):
SELECT UserID, SUM(km) total
FROM (
SELECT t1.UserID,
DEGREES(ACOS(LEAST(1.0, COS(RADIANS(t1.Lat))
* COS(RADIANS(t2.Lat))
* COS(RADIANS(t1.Lon- t2.Lon))
+ SIN(RADIANS(t1.Lat))
* SIN(RADIANS(t2.Lat))))) km
FROM table t1
JOIN table t2 ON t1.UserID = t2.UserID and t1.StepID = t2.StepID - 1
) t
GROUP BY UserID
I've got the formula from: https://stackoverflow.com/a/24372831/2244262

MySQL MIN query not working for calculated distance

I have a table of locations in my database. I need a query to find out the nearest location, provided any coordinates. I wrote the following query to get all rows, along with their respective distance from a given coordinate(distance in meters):
SELECT *, 111111 * DEGREES(ACOS(LEAST(COS(RADIANS(dest.latitude)) * COS(RADIANS(8.584710)) * COS(RADIANS(dest.longitude - 76.868735)) + SIN(RADIANS(dest.latitude)) * SIN(RADIANS(8.584710)), 1.0))) as distance FROM offer dest;
It gives the following output:
+----+------------------------+----------+-----------+------------+---------------------+
| id | description | latitude | longitude | name | distance |
+----+------------------------+----------+-----------+------------+---------------------+
| 2 | Location 1 Description | 8.574858 | 76.874748 | Location 1 | 1278.565430298969 |
| 12 | Location 2 Description | 8.584711 | 76.868738 | Location 2 | 0.35494725284463646 |
+----+------------------------+----------+-----------+------------+---------------------+
It is all working fine. Now to get the Minimum distance, I added HAVING MIN(distance) to this query. Now the query looks like below:
SELECT *, 111111 * DEGREES(ACOS(LEAST(COS(RADIANS(dest.latitude)) * COS(RADIANS(8.584710)) * COS(RADIANS(dest.longitude - 76.868735)) + SIN(RADIANS(dest.latitude)) * SIN(RADIANS(8.584710)), 1.0))) as distance FROM offer dest having MIN(distance);
Now, this query is supposed to return 1 row and that should be Location 2, as it has the the minimum location, but this is returning location 1 instead as seen below:
+----+------------------------+----------+-----------+------------+---------------------+
| id | description | latitude | longitude | name | distance |
+----+------------------------+----------+-----------+------------+---------------------+
| 2 | Location 1 Description | 8.574858 | 76.874748 | Location 1 | 1278.565430298969 |
+----+------------------------+----------+-----------+------------+---------------------+
Why is this behaving so? Is there something wrong with my query? IF yes, what is it and how do I get the location with minimum distance.
A HAVING-clause is used to filter conditions for a group. A group is defined with an aggregate function in SELECT-part and with a GROUP BY. As you do not have either of those, you should not use HAVING.
If you want to show the minimum distance from set of rows order by the distance and limit the result set just to one row.
SELECT *,
111111 * DEGREES(ACOS(LEAST(COS(RADIANS(dest.latitude)) *
COS(RADIANS(8.584710)) * COS(RADIANS(dest.longitude - 76.868735)) +
SIN(RADIANS(dest.latitude)) * SIN(RADIANS(8.584710)), 1.0))) as distance
FROM offer dest
ORDER BY distance
LIMIT 1;

How to get nearest coordinates from database in mysql?

I have got a table with id,latitude (lat),longitude (lng),altitude (alt).
I have some coordinates and I would like to find the closest entry in the DB.
I used this but not yet working correctly:
SELECT lat,ABS(lat - TestCordLat), lng, ABS(lng - TestCordLng), alt AS distance
FROM dhm200
ORDER BY distance
LIMIT 6
I have a table with the 6 nearest points displaying me the lattitude, longtitude and altitude.
Query to get nearest distance in kilometer (km) from mysql:
SELECT id, latitude, longitude, SQRT( POW(69.1 * (latitude - 4.66455174) , 2) + POW(69.1 * (-74.07867091 - longitude) * COS(latitude / 57.3) , 2)) AS distance FROM ranks ORDER BY distance ASC;
You may wish to limit radius by HAVING syntax.
... AS distance FROM ranks HAVING distance < '150' ORDER BY distance ASC;
Example:
mysql> describe ranks;
+------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| latitude | decimal(10,8) | YES | MUL | NULL | |
| longitude | decimal(11,8) | YES | | NULL | |
+------------+---------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
mysql> SELECT id, latitude, longitude, SQRT( POW(69.1 * (latitude - 4.66455174) , 2) + POW(69.1 * (-74.07867091 - longitude) * COS(latitude / 57.3) , 2)) AS distance FROM ranks ORDER BY distance ASC;
+----+-------------+--------------+--------------------+
| id | latitude | longitude | distance |
+----+-------------+--------------+--------------------+
| 4 | 4.66455174 | -74.07867091 | 0 |
| 10 | 4.13510880 | -73.63690401 | 47.59647003096195 |
| 11 | 6.55526689 | -73.13373892 | 145.86590936973073 |
| 5 | 6.24478548 | -75.57050110 | 149.74731096011348 |
| 7 | 7.06125013 | -73.84928550 | 166.35723903407165 |
| 9 | 3.48835279 | -76.51532198 | 186.68173882319724 |
| 8 | 7.88475514 | -72.49432589 | 247.53456848808233 |
| 1 | 60.00001000 | 101.00001000 | 7156.836171031409 |
| 3 | 60.00001000 | 101.00001000 | 7156.836171031409 |
+----+-------------+--------------+--------------------+
9 rows in set (0.00 sec)
You will need to use the Haversine formula to calculate distances taking into account the latitude and longitude:
dlon = lon2 - lon1
dlat = lat2 - lat1
a = (sin(dlat/2))^2 + cos(lat1) * cos(lat2) * (sin(dlon/2))^2
c = 2 * atan2( sqrt(a), sqrt(1-a) )
distance = R * c (where R is the radius of the Earth)
However, the altitude raises the difficulty of the problem. If between point A and point B, having different altitudes the road contains a lot of high altitude differences, then assuming that the altitude's line's derivative between the two points is unchanged might be misleading, not taking that into account at all might be very misleading. Compare the distance between a point in China and a point in India, having the Himalaja in between with the distance between two points on the surface of the Pacific ocean. A possibility would be to vary R to be the average of the altitudes for each comparisons, but in case of large distances this could be misleading, as discussed earlier.

Finding the min distance MYSQL

I want to find the min distance between (x2-x1)^2 + (y2-y1)^2 for a particular macId and timeStamp. I'm trying to find a nearest possible gate
location for an individual at one particular instance of time. So, the query should return one unique value of user at one instance of time that is min to the GATE location.
The data set looks like:
X1 Y1 TimeStamp MACID X2 Y2 Gate
| 5618 | 5303 |1 12:22:02 | 54:ea:a8:53:5b:eb | 5844 | 5377 | C24
| 5848 | 5046 |1 12:22:02 | 54:ea:a8:53:5b:eb | 5844 | 5377 | C18
| 6094 | 5464 |1 12:22:02 | 54:ea:a8:53:5b:eb | 5844 | 5377 | C17
| 6021 | 6540 |1 13:09:48 | 48:5a:3f:6a:01:b9 | 6210 | 6801 | C23
| 6366 | 7036 |1 13:09:48 | 48:5a:3f:6a:01:b9 | 6210 | 6801 | C14
| 6366 | 7036 |1 13:09:48 | 48:5a:3f:6a:01:b9 | 6210 | 6801 | C13
The result set should look like below:
X1 Y1 TimeStamp MACID X2 Y2 Gate
| 5848 | 5046 |1 12:22:02 | 54:ea:a8:53:5b:eb | 5844 | 5377 | C18
| 6021 | 6540 |1 13:09:48 | 48:5a:3f:6a:01:b9 | 6210 | 6801 | C23
I have tried this below query but not working:
select min((x2-x1)^2 + (y2-y1)^2), macID, timeStamp from maptable
groupbymacID, timeStamp
I also tried using self joins but seems completely wrong.
May I know where I'm going wrong.
You could use this query:
SELECT m.*
FROM maptable m, (
SELECT TimeStamp, macid, MIN(POW((x2-x1), 2) + POW((y2-y1), 2)) mindist
FROM maptable
GROUP BY TimeStamp, macid
) a
WHERE m.TimeStamp = a.TimeStamp AND m.macid = a.macid
AND POW((x2-x1), 2) + POW((y2-y1), 2) = a.mindist;
SQL Fiddle: http://sqlfiddle.com/#!9/d7979/6
But note that it does not return one row per macid and date, because in your input data the two final rows are the same, so the min distance is the same for gates C13 and C14

Selecting Unique records

I'm working on a Geo/Spatial search where I'm looking for nearby points. I have this Haversine query being run against a table:
SELECT
uid, adrLat, adrLng,
round(3956 * 2 * ASIN(SQRT(POWER(SIN((39.97780609 - abs(adrLat)) * pi() / 180 / 2), 2) + COS(39.97780609 * pi() / 180) * COS(abs(adrLat) * pi() / 180) * POWER(SIN((-105.25861359 - adrLng) * pi() / 180 / 2), 2))), 2) AS distance
FROM dataPoints
WHERE adrLng BETWEEN -105.2680699902 AND -105.2491571898
AND adrLat BETWEEN 39.970559713188 AND 39.985052466812
HAVING distance <= 0.30 and distance > 0.00
ORDER BY distance;
This would give me a result much like this:
+-----+-------------+---------------+----------+
| uid | adrLat | adrLng | distance |
+-----+-------------+---------------+----------+
| 191 | 39.97764587 | -105.25627136 | 0.12 |
| 520 | 39.97746658 | -105.25627136 | 0.13 |
| 265 | 39.97560120 | -105.25814056 | 0.15 |
| 266 | 39.97560120 | -105.25814056 | 0.15 |
| 274 | 39.97710037 | -105.25589752 | 0.15 |
| 98 | 39.97764969 | -105.26172638 | 0.17 |
| 576 | 39.97967911 | -105.25613403 | 0.18 |
| 575 | 39.97967911 | -105.25613403 | 0.18 |
| 469 | 39.97895813 | -105.25386810 | 0.26 |
| 470 | 39.97895813 | -105.25386810 | 0.26 |
| 1 | 39.98003006 | -105.25471497 | 0.26 |
| 383 | 39.97621155 | -105.26350403 | 0.28 |
| 431 | 39.97459793 | -105.25507355 | 0.29 |
| 430 | 39.97459793 | -105.25507355 | 0.29 |
| 429 | 39.97459793 | -105.25507355 | 0.29 |
| 428 | 39.97459793 | -105.25507355 | 0.29 |
+-----+-------------+---------------+----------+
However, as you can probably tell, some records are duplicated in the table (that's the way the data is provided to me, and I have to retain it that way.) 265:266, 576:575, 469:470, and 431-428 are all duplicates.
Is there a way to modify the query to pick unique records only? It looks like I have to match adrLat and adrLng to filter duplicates out, but I'm not sure if I can do it all within the same query, or if I have to do some post processing on the result.
SELECT adrLat, adrLng,
round(3956 * 2 * ASIN(SQRT(POWER(SIN((39.97780609 - abs(adrLat)) * pi() / 180 / 2), 2) + COS(39.97780609 * pi() / 180) * COS(abs(adrLat) * pi() / 180) * POWER(SIN((-105.25861359 - adrLng) * pi() / 180 / 2), 2))), 2) AS distance
FROM mytable
WHERE adrLng BETWEEN -105.2680699902 AND -105.2491571898
AND adrLat BETWEEN 39.970559713188 AND 39.985052466812
GROUP BY
adrLat, adrLng
HAVING distance <= 0.30
AND distance > 0.00
ORDER BY
distance
SELECT DISTINCT colum_name FROM table
The SELECT keyword allows us to grab all information from a column (or columns) on a table. This, of course, necessarily mean that there will be redundancies. What if we only want to select each DISTINCT element? This is easy to accomplish in SQL. All we need to do is to add DISTINCT after SELECT. The syntax is as follows:
I still need all four columns returned
You've already got the unique data there (e.g. uid 576 and 575 return the same coordinates - but the uid is obviously different).
Your definition of 'unique' is obviously different from ours - can you provide an example of wht you expect to see?