1

假设我有一个像这样的 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

我尝试使用很多“工会”来实现这一目标。但是我如何以更少的复杂性和代码大小来做到这一点?

4

2 回答 2

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   

row_number在 unpivot 结果中添加了一个伪列,并用它来将值拆分为两列之一;然后用于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(或任何它们)是固定的,您可以使用它们而不是rn,但我觉得它们可能会改变。


作为一种不需要的变化,您可以比较不同列中的值,排序。假设数据设置如下:

insert into t42 values (105, 'blue', 'green', 'black', 'red');
insert into t42 values (106, 'red', 'green', 'white', 'blue');

...所以现在两行都有redand 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 列的特殊情况,但是您可能不希望显示它。将blackandwhite或任何其他不匹配的对放入同一结果行将需要另一个级别的操作。

于 2013-07-26T18:11:07.850 回答
0

你想使用枢轴。您可以在 11g 中查看有关枢轴的 Oracle 信息

我还没有访问 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

这是另一个可能有帮助的答案

或者更好,这里是SO 上所有 PIVOT 和 ORACLE 标记的项目

最后,在 Oracle 论坛上找到了另一个可能有助于创建简单转置的线程。底部有 2 个“有用”的答案。

于 2013-07-26T14:37:40.797 回答