假设我有一个像这样的2x5表。
105 blue green black red
106 red green white red
我想转置此表并比较两列中的值,并显示它们在第三列中是否相同。就像这样
105 106 notsame
blue red notsame
green green same
black white notsame
red red same
我尝试使用很多“工会”来实现这一目标。但是我如何以较低的复杂性和代码大小来实现呢?
答案 0 :(得分:2)
您需要将列放入行中,这意味着您需要将它们取消。我正在使用一个定义为:
的表create table t42(id number, colour1 varchar2(5), colour2 varchar2(5),
colour3 varchar2(5), colour4 varchar2(5));
使用您的两行数据,您可以将其取消为10行,每行每列一行:
select *
from (
select id, to_char(id) as cid, colour1, colour2, colour3, colour4
from t42
)
unpivot (val for col in (cid, colour1, colour2, colour3, colour4));
ID COL VAL
---------- ------- ----------------------------------------
105 CID 105
105 COLOUR1 blue
105 COLOUR2 green
105 COLOUR3 black
105 COLOUR4 red
106 CID 106
106 COLOUR1 red
106 COLOUR2 green
106 COLOUR3 white
106 COLOUR4 red
然后,您可以有效地重新调整:
select col,
max(case when rn = 1 then val end) as val1,
max(case when rn = 2 then val end) as val2
from (
select u.*, row_number() over (partition by u.col order by u.id) as rn
from (
select id, to_char(id) as cid, colour1, colour2, colour3, colour4
from t42
) unpivot (val for col in (cid, colour1, colour2, colour3, colour4)) u
)
group by col;
COL VAL1 VAL2
------- ----- -----
CID 105 106
COLOUR1 blue red
COLOUR2 green green
COLOUR3 black white
COLOUR4 red red
我在unpivot结果中添加了row_number
伪列,并使用它将值拆分为两列中的一列;然后使用max
折叠空值。
然后你只需要在两列中填充值:
select val1, val2,
case when val1 = val2 then 'same' else 'notsame' end as compare
from (
select col,
max(case when rn = 1 then val end) as val1,
max(case when rn = 2 then val end) as val2
from (
select u.*, row_number() over (partition by u.col order by u.id) as rn
from (
select id, to_char(id) as cid, colour1, colour2, colour3, colour4
from t42
) unpivot (val for col in (cid, colour1, colour2, colour3, colour4)) u
)
group by col
);
VAL1 VAL2 COMPARE
----- ----- -------
105 106 notsame
blue red notsame
green green same
black white notsame
red red same
如果添加更多列,则只需修改内部的unpivot部分。
我说你有效地重新转动,但你实际上也可以重新转动;我认为另一种方式看起来是实现性的,但这可能表现得更好,意见也会有所不同:
select a_val, b_val,
case when a_val = b_val then 'same' else 'notsame' end as compare
from (
select * from (
select col, val, rn
from (
select u.*, row_number() over (partition by u.col order by u.id) as rn
from (
select id, to_char(id) as cid, colour1, colour2, colour3, colour4
from t42
) unpivot (val for col in (cid, colour1, colour2, colour3, colour4)) u
)
)
pivot (max(val) as val for (rn) in (1 as a, 2 as b))
);
A_VAL B_VAL COMPARE
----- ----- -------
105 106 notsame
blue red notsame
green green same
black white notsame
red red same
如果ID(或其他任何内容)已修复,您可以使用这些ID而不是rn
,但我会得到他们可能会改变的印象。
作为unasked-for变体,您可以比较不同列中的值,排序。假设数据设置如下:
insert into t42 values (105, 'blue', 'green', 'black', 'red');
insert into t42 values (106, 'red', 'green', 'white', 'blue');
...所以现在两行都有red
和blue
,但是在不同的列中。您可以根据名称对值进行排名,而不是使用列名称:
select val1, val2,
case when val1 = val2 then 'same' else 'notsame' end as compare
from (
select col_rnk,
max(case when rn = 1 then val end) as val1,
max(case when rn = 2 then val end) as val2
from (
select u.*,
row_number() over (partition by u.col order by u.id) as rn,
rank() over (order by case when u.col = 'CID' then null else u.val end)
as col_rnk
from (
select id, to_char(id) as cid, colour1, colour2, colour3, colour4
from t42
) unpivot (val for col in (cid, colour1, colour2, colour3, colour4)) u
)
group by col_rnk
)
order by val1;
VAL1 VAL2 COMPARE
----- ----- -------
105 106 notsame
black notsame
blue blue same
green green same
red red same
white notsame
我刚刚添加了col_rnk
伪列; ID列的特殊情况,但您可能不希望显示该列。将black
和white
或任何其他不匹配的对进入同一结果行需要进行另一级别的操作。
答案 1 :(得分:0)
您想使用枢轴。您可以查看Oracle info on pivot in 11g
我还没有访问11g,所以我无法解决一个例子。但我知道它,那就是你想要的。
这是来自该链接的SQL:
select * from (
select times_purchased, state_code
from customers t
)
pivot
(
count(state_code)
for state_code in ('NY','CT','NJ','FL','MO')
)
order by times_purchased
或者更好,这里有PIVOT and ORACLE tagged items on SO
最后,发现another thread on Oracle's forum可能有助于创建简单的转置。底部有2个“有用的”答案。