如何获得第二大的列值和列名

问题描述

如何获取第二大列值及其名称

我当前的查询在大多数情况下都正确,但是在最大和第二个最大值相同的情况下,我得到的值是错误的。

    select item_code,A,B,C,greatest(A,C) as largest1,greatest(case when largest1 = A then 0 else A end,case when largest1 = B then 0 else B end,case when largest1 = C then 0 else C end) as largest2,(case largest1 when A then 'A'
                       when B then 'B'
                       when C then 'C' end) as largest1_column_name,(case largest2 when A then 'A'
                       when B then 'B'
                       when C then 'C' else 'None' end) as largest2_column_name 
        from table1 

下面是示例表:

+-----------+----+----+----+
| item_code | A  | B  | C  |
+-----------+----+----+----+
| p1        | 20 | 30 | 40 |
| p2        | 50 | 30 | 10 |
| p3        | 30 | 50 | 10 |
| p4        | 30 | 30 | 30 |
| p5        | 50 | 50 | 10 |
| p6        |  0 |  0 |  0 |
+-----------+----+----+----+

以下是预期的输出

+-----------+----+----+----+----------+----------+----------------------+----------------------+
| item_code | A  | B  | C  | largest1 | largest2 | largest1_column_name | largest2_column_name |
+-----------+----+----+----+----------+----------+----------------------+----------------------+
| p1        | 20 | 30 | 40 |       40 |       30 | C                    | B                    |
| p2        | 50 | 30 | 10 |       50 |       30 | A                    | B                    |
| p3        | 30 | 50 | 10 |       50 |       30 | B                    | A                    |
| p4        | 30 | 30 | 30 |       30 |       30 | A                    | B                    |
| p5        | 50 | 50 | 10 |       50 |       50 | A                    | B                    |
| p6        |  0 |  0 |  0 |        0 |        0 | A                    | B                    |
+-----------+----+----+----+----------+----------+----------------------+----------------------+

这是我从查询中得到的输出(我将注释标记错误):

+-----------+----+----+----+----------+-------------+----------------------+----------------------+
| item_code | A  | B  | C  | largest1 |  largest2   | largest1_column_name | largest2_column_name |
+-----------+----+----+----+----------+-------------+----------------------+----------------------+
| p1        | 20 | 30 | 40 |       40 | 30          | C                    | B                    |
| p2        | 50 | 30 | 10 |       50 | 30          | A                    | B                    |
| p3        | 30 | 50 | 10 |       50 | 30          | B                    | A                    |
| p4        | 30 | 30 | 30 |       30 | 0/*wrong*/  | A                    | NULL/*wrong*/        |
| p5        | 50 | 50 | 10 |       50 | 10/*wrong*/ | A                    | C/*wrong*/           |
| p6        |  0 |  0 |  0 |        0 | 0/*wrong*/  | A                    | A/*wrong*/           |
+-----------+----+----+----+----------+-------------+----------------------+----------------------+

解决方法

通过取消旋转行,对值进行排名然后使用条件聚合,可以更简单地实现此目的。在Postgres中,您可以这样表达:

select t.*,x.*
from table1 t1
cross join lateral (
    select 
        min(val) filter(where rn = 1) largest1,min(val) filter(where rn = 2) largest2,min(col) filter(where rn = 1) largest1_column_name,min(col) filter(where rn = 2) largest2_column_name
    from (
        select x.*,dense_rank() over(order by val desc) rn
        from (values ('a',a),('b',b),('c',c)) as x(col,val)
    ) x
) x
,

我在Snowflake中尝试使用此变量(listagg而不是string_agg进行了细微改动,它似乎获得了预期的结果

with cte (item_code,abc,id) as
(select item_code,a,'a' from table1 union all
 select item_code,b,'b' from table1 union all
 select item_code,c,'c' from table1)
 
select item_code,max(case when id='a' then abc end) a,max(case when id='b' then abc end) b,max(case when id='c' then abc end) c,split_part(string_agg(abc::varchar,',' order by abc desc),1) largest1,2) largest2,split_part(string_agg(id,1) largest1_col,2) largest2_col
from cte
group by item_code;