如何在 Oracle SQL 中对同一行的列进行排名
How to rank columns of a same row in Oracle SQL
我在 table 中有以下行:
C1 |C2 |C3 |C4
7 | 3 | 1 | 6
我想构建一个查询,使每一列都有一个顺序;
O1 |O2 |O3 |O4
4 | 2 | 1 | 3
是否可以在单个查询中进行这种逐行比较?或者是构建复杂 case when 语句的唯一选择?
编辑:我试图绕过的情况:
case
when C1 = greatest ( C1, C2, C3, C4) then 1
when C1 >= C2 and C1 >= C3 and C1 < C4
or C1 >= C2 and C1 < C3 and C1 >= C4
or C1 < C2 and C1 >= C3 and C1 >= C4 then 2
when C1 >= C2 and C1 < C3 and C1 < C4
or C1 < C2 and C1 >= C3 and C1 < C4
or C1 < C2 and C1 < C3 and C1 >= C4 then 3
when C1 = least (C1, C2, C3, C4 ) then 4
end as O1
如果值相等,索引决定顺序:如果C2 = C3,O2 = 1,O2 = 3。
如您所见,这很容易出错。有没有办法让这个比较更优雅?
只需要在单行中进行比较,单行中的列顺序不会影响 table 中行的顺序。
编辑 2:table 中有多行,用 ID_ROW 标识。
我想你的意思是连续使用 unpivot
和 pivot
:
create table mytable(C1 int, C2 int, C3 int, C4 int );
insert into mytable values(7, 3, 1, 6);
select * from
(
select CX variable, dense_rank() over ( order by value ) value
from mytable
unpivot (value for CX in (C1, C2, C3, C4))
)
pivot
(
max(value) as vl for(variable) in ('C1' AS C1, 'C2' AS C2, 'C3' AS C3, 'C4' AS C4 )
);
C1_VL C2_VL C3_VL C4_VL
4 2 1 3
复杂的嵌套 case-when 不是必需的,您可以使用 "simple" case-when 和求和(虽然有点乏味)
select t.*,
case when c1>c2 then 1 else 0 end
+ case when c1>c3 then 1 else 0 end
+ case when c1>c4 then 1 else 0 end + 1 as q1,
case when c2>c1 then 1 else 0 end
+ case when c2>c3 then 1 else 0 end
+ case when c2>c4 then 1 else 0 end + 1 as q2,
case when c3>c1 then 1 else 0 end
+ case when c3>c2 then 1 else 0 end
+ case when c3>c4 then 1 else 0 end + 1 as q3 ,
case when c4>c1 then 1 else 0 end
+ case when c4>c2 then 1 else 0 end
+ case when c4>c3 then 1 else 0 end + 1 as q4
FROM table1 t;
| c1 | c2 | c3 | c4 | q1 | q2 | q3 | q4 |
|----|----|----|----|----|----|----|----|
| 7 | 3 | 1 | 6 | 4 | 2 | 1 | 3 |
| 6 | 5 | 4 | 1 | 4 | 3 | 2 | 1 |
一种方法是逆透视数据,使用 ROW_NUMBER() 分析函数并重新透视。这种方法可能比直接处理每一行花费更长的时间,但更容易维护。您需要决定哪个目标更重要。
EDIT - 基于所需的领带处理(仍不清楚),也许 ROW_NUMBER() 应替换为 RANK() 或 DENSE_RANK();否则解决方案是相同的。 结束编辑
create table inputs ( id_row, c1, c2, c3, c4 ) as
select 101, 7, 3, 1, 6 from dual union all
select 102, 1, 5, 5, 2 from dual union all
select 103, 0, 0, 0, 0 from dual union all
select 104, 8, 3, 4, 1 from dual
;
select id_row,
c1_val as c1, c2_val as c2, c3_val as c3, c4_val as c4,
c1_rn as o1, c2_rn as o2, c3_rn as o3, c4_rn as o4
from (
select id_row, val, col,
row_number() over
(partition by id_row order by val, col) as rn
from inputs
unpivot ( val for col in (c1 as 1, c2 as 2, c3 as 3, c4 as 4) )
)
pivot ( min(val) as val, min(rn) as rn
for col in (1 as c1, 2 as c2, 3 as c3, 4 as c4) )
;
ID_ROW C1 C2 C3 C4 O1 O2 O3 O4
-------- ------ ------ ------ ------ ------ ------ ------ ------
101 7 3 1 6 4 2 1 3
102 1 5 5 2 1 3 4 2
103 0 0 0 0 1 2 3 4
104 8 3 4 1 4 2 3 1
我在 table 中有以下行:
C1 |C2 |C3 |C4
7 | 3 | 1 | 6
我想构建一个查询,使每一列都有一个顺序;
O1 |O2 |O3 |O4
4 | 2 | 1 | 3
是否可以在单个查询中进行这种逐行比较?或者是构建复杂 case when 语句的唯一选择?
编辑:我试图绕过的情况:
case
when C1 = greatest ( C1, C2, C3, C4) then 1
when C1 >= C2 and C1 >= C3 and C1 < C4
or C1 >= C2 and C1 < C3 and C1 >= C4
or C1 < C2 and C1 >= C3 and C1 >= C4 then 2
when C1 >= C2 and C1 < C3 and C1 < C4
or C1 < C2 and C1 >= C3 and C1 < C4
or C1 < C2 and C1 < C3 and C1 >= C4 then 3
when C1 = least (C1, C2, C3, C4 ) then 4
end as O1
如果值相等,索引决定顺序:如果C2 = C3,O2 = 1,O2 = 3。
如您所见,这很容易出错。有没有办法让这个比较更优雅?
只需要在单行中进行比较,单行中的列顺序不会影响 table 中行的顺序。
编辑 2:table 中有多行,用 ID_ROW 标识。
我想你的意思是连续使用 unpivot
和 pivot
:
create table mytable(C1 int, C2 int, C3 int, C4 int );
insert into mytable values(7, 3, 1, 6);
select * from
(
select CX variable, dense_rank() over ( order by value ) value
from mytable
unpivot (value for CX in (C1, C2, C3, C4))
)
pivot
(
max(value) as vl for(variable) in ('C1' AS C1, 'C2' AS C2, 'C3' AS C3, 'C4' AS C4 )
);
C1_VL C2_VL C3_VL C4_VL
4 2 1 3
复杂的嵌套 case-when 不是必需的,您可以使用 "simple" case-when 和求和(虽然有点乏味)
select t.*,
case when c1>c2 then 1 else 0 end
+ case when c1>c3 then 1 else 0 end
+ case when c1>c4 then 1 else 0 end + 1 as q1,
case when c2>c1 then 1 else 0 end
+ case when c2>c3 then 1 else 0 end
+ case when c2>c4 then 1 else 0 end + 1 as q2,
case when c3>c1 then 1 else 0 end
+ case when c3>c2 then 1 else 0 end
+ case when c3>c4 then 1 else 0 end + 1 as q3 ,
case when c4>c1 then 1 else 0 end
+ case when c4>c2 then 1 else 0 end
+ case when c4>c3 then 1 else 0 end + 1 as q4
FROM table1 t;
| c1 | c2 | c3 | c4 | q1 | q2 | q3 | q4 |
|----|----|----|----|----|----|----|----|
| 7 | 3 | 1 | 6 | 4 | 2 | 1 | 3 |
| 6 | 5 | 4 | 1 | 4 | 3 | 2 | 1 |
一种方法是逆透视数据,使用 ROW_NUMBER() 分析函数并重新透视。这种方法可能比直接处理每一行花费更长的时间,但更容易维护。您需要决定哪个目标更重要。
EDIT - 基于所需的领带处理(仍不清楚),也许 ROW_NUMBER() 应替换为 RANK() 或 DENSE_RANK();否则解决方案是相同的。 结束编辑
create table inputs ( id_row, c1, c2, c3, c4 ) as
select 101, 7, 3, 1, 6 from dual union all
select 102, 1, 5, 5, 2 from dual union all
select 103, 0, 0, 0, 0 from dual union all
select 104, 8, 3, 4, 1 from dual
;
select id_row,
c1_val as c1, c2_val as c2, c3_val as c3, c4_val as c4,
c1_rn as o1, c2_rn as o2, c3_rn as o3, c4_rn as o4
from (
select id_row, val, col,
row_number() over
(partition by id_row order by val, col) as rn
from inputs
unpivot ( val for col in (c1 as 1, c2 as 2, c3 as 3, c4 as 4) )
)
pivot ( min(val) as val, min(rn) as rn
for col in (1 as c1, 2 as c2, 3 as c3, 4 as c4) )
;
ID_ROW C1 C2 C3 C4 O1 O2 O3 O4
-------- ------ ------ ------ ------ ------ ------ ------ ------
101 7 3 1 6 4 2 1 3
102 1 5 5 2 1 3 4 2
103 0 0 0 0 1 2 3 4
104 8 3 4 1 4 2 3 1