How to select distinct one column but return all columns with acos-cos-radians formula

耗尽温柔 提交于 2020-06-27 04:39:33

问题


I want to fetch a table named supplier_products which has following columns

ID Item_id supplier_id variant_id  price    lat        lng   serving_radius(in km)

1     1      2            12       22.00  26.11360000 85.39430000    1
2     1      3            12       44.00  26.11360000 85.39430000    4
3     1      2            13       25.00  26.11360000 85.39430000    4
4     1      3            13       23.00  26.11360000 85.39430000    4

Now for searching supplier products near a latitude and longitude say($lat = 26.1136;$long = 85.3643;) . i was using this query

SELECT *, (6371 * acos(cos(radians('$lat')) * cos(radians(lat)) * cos( radians(lng) - radians('$long')) + sin(radians('$lat')) * sin(radians(lat)))) AS distance FROM supplier_products HAVING distance <= serving_radius ORDER BY distance")

The above query returns all rows which serves the input $lat & $long.

But now i want to return all columns of only those rows having distinct variant_id which serves the input $lat & $long

I tried using a GROUP BY -

SELECT *, (6371 * acos(cos(radians('$lat')) * cos(radians(lat)) * cos( radians(lng) - radians('$long')) + sin(radians('$lat')) * sin(radians(lat)))) AS distance FROM supplier_products GROUP BY variant_id HAVING distance <= serving_radius")

But it eliminates some desired rows as GROUP BY is being performed before the HAVING clause. So it eliminates some required rows which are in the serving radius.

I am using PHP & MYSQL

EDIT- I want this as my output

ID Item_id supplier_id variant_id  price    lat        lng   serving_radius(in km)

2     1      3            12       44.00  26.11360000 85.39430000    4
3     1      2            13       25.00  26.11360000 85.39430000    4

As row with ID-1 does not serves the input $lat/$long

But my attempt gave the following result -

ID Item_id supplier_id variant_id  price    lat        lng   serving_radius(in km)

3     1      2            13       25.00  26.11360000 85.39430000    4

Because GROUP BY eliminated the 2nd row


回答1:


Your requirement is still incomplete if you want predictable results. You call for choosing just one row from among several. Why did your desired result show id=3 and not id=4? I guess you chose one of the two at random.

This query (https://www.db-fiddle.com/f/USMrhc8gLcRD2rAmuzYzH/0) does that for you.

SET @lat := 26.120888;
SET @long := 85.364832;
SELECT ANY_VALUE(supplier_id), variant_id, ANY_VALUE(price)  
FROM  ( SELECT ID,
               (6371 * acos(cos(radians(@lat)) * cos(radians(lat)) *
                cos( radians(lng) - radians(@long)) + sin(radians(@lat)) * 
                sin(radians(lat)))) AS distance
          FROM supplier_products 
     ) t WHERE distance < serving_radius
GROUP BY variant_id

It has a subquery to show a variant of your table that shows distances from the @lat and @long values supplied. It shows ID and distance. This is it. (https://www.db-fiddle.com/f/guagWYVXXaf7cPkbojj9KD/2)

        SELECT *, 
               (6371 * acos(cos(radians(@lat)) * cos(radians(lat)) *
                cos( radians(lng) - radians(@long)) + sin(radians(@lat)) * 
                sin(radians(lat)))) AS distance
          FROM supplier_products 

It then uses that subquery in an outer query with a GROUP BY, see above.

But, because you want just one row per variant and you haven't told us how to pick that row if there are multiple rows, the query uses the ANY_VALUE() function to choose one of the available values for each column in your result.

Versions of MySQL older than 8.0 don't need the ANY_VALUE() functions, because of MySQL's notorious nonstandard handling of GROUP BY: please read this.

Beware: if you let MySQL use this ANY_VALUE() stuff, whether implicit or explicit, you will drive your testers and your users crazy. They'll sometimes get different results today than they got last week, and they'll wonder what they did wrong. Please don't do it.

And one other thing: your distance formula is notorious for throwing exceptions on very small distances. Make sure it never tries to use ACOS() with a value greater than 1 by modifying it like this to use LEAST(). (https://www.db-fiddle.com/f/USMrhc8gLcRD2rAmuzYzH/1)

    (6371 * acos(LEAST(1.0,cos(radians(@lat)) * cos(radians(lat)) *
    cos( radians(lng) - radians(@long)) + sin(radians(@lat)) *
    sin(radians(lat)))))


来源:https://stackoverflow.com/questions/61387232/how-to-select-distinct-one-column-but-return-all-columns-with-acos-cos-radians-f

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!