Skip to content
Advertisement

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

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

Advertisement

Answer

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)))))
User contributions licensed under: CC BY-SA
3 People found this is helpful
Advertisement