Home > Software design >  Get Distinct Rows By Two Columns With Ordering Desc
Get Distinct Rows By Two Columns With Ordering Desc

Time:07-09

I have ms sql table like following.

ID Num1 Num2 Range
3 1 2 1
4 2 1 2
5 3 1 1
6 2 3 1
7 4 1 1
8 1 4 2
9 1 4 3

I want to get distinct rows by num1 & num2 with highest range. Query should return result like this.

ID Num1 Num2 Range
4 2 1 2
5 3 1 1
6 2 3 1
9 1 4 3

Num1 & Num2 values are grouped (please note that 2,1 and 1,2 are considered as same). If the ere are multiple 1,2 rows with the highest range is selected from that group.

So far I could return following but it contains Num1 & Num2 swapped rows (1,2 & 2,1...).

Query should return the column as in the 1st table and Num1&Num2 data cannot be swapped.

ID Num1 Num2 Range
3 1 2 1
4 2 1 2
5 3 1 1
6 2 3 1
7 4 1 1
9 1 4 3

Query I've tried

Select Max([ID]) As 'ID'
      ,Max([Num1]) As 'Num1'
      ,Max([Num2]) As 'Num2'
      ,Max([Range]) As 'Range'
    From [dbo].[Range]
    Group by Concat([Num1], [Num2])
    Order By Range desc

CodePudding user response:

We can use a least/greatest trick here along with aggregation. Actually, we can't do that on SQL Server, which doesn't support these scalar functions, but we can still use CASE expressions:

WITH cte AS (
    SELECT *, MAX([Range]) OVER (PARTITION BY 
                  CASE WHEN Num1 < Num2 THEN Num1 ELSE Num2 END,
                  CASE WHEN Num1 < Num2 THEN Num2 ELSE Num1 END) AS MaxRange
    FROM yourTable
),
cte2 AS (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY 
                  CASE WHEN Num1 < Num2 THEN Num1 ELSE Num2 END,
                  CASE WHEN Num1 < Num2 THEN Num2 ELSE Num1 END
                  ORDER BY MaxRange DESC) rn
    FROM cte
)

SELECT ID, Num1, Num2, [Range]
FROM cte2
WHERE rn = 1
ORDER BY ID;

CodePudding user response:

Provided you're using SQLServer 2017 you can use string_agg via an apply to create the condensed groupings required (this would easily support more than 2 columns if required) and a row_number solution for ordering by range:

with g as (
  select *, Row_Number() over(partition by grp order by range desc)rn
  from t
  cross apply (
    select String_Agg(n, '') within group(order by n) 
    from (values(num1), (num2))v(n) 
  )s(grp)
)
select Id, num1, num2, Range
from g
where rn = 1
order by id;

See Demo Fiddle

Edit:

on SQL server 2014 you could substitute for the for xml path hack:

with g as (
  select *, Row_Number() over(partition by grp order by range desc)rn
  from t
  cross apply (
    select top(2) n
    from (values(num1), (num2))v(n) 
    order by n
    for xml path('')
  )s(grp)
)
select Id, num1, num2, Range
from g
where rn = 1
order by id;
  • Related