如何将具有重复值的行转换为列?

Posted

技术标签:

【中文标题】如何将具有重复值的行转换为列?【英文标题】:How do I turn rows with duplicate values to columns? 【发布时间】:2018-11-30 12:55:39 【问题描述】:

我尝试旋转我的表但保留额外的行(在我的示例 eeeeee 中)Oracle SQL 中有没有办法做到这一点?

select * from (
select 
    mat_table.material, attribute_table.attribute, attribute_table.value
  from 
    mat_table mat_table
    inner join 
    attribute_table on mat_table.rel= attribute_table.rel
    where 




      material = 'Material_A' 
            )


material    |attribute|  value
_____________________________________

Material_A  |aaaaaa    | 
Material_A  |bbbbbb    |       hello
Material_A  |cccccc    |       val_1
Material_A  |dddddd    |       2
Material_A  |eeeeee    |       15
Material_A  |eeeeee    |       16
Material_A  |eeeeee    |       24 

当我在 where 子句下使用 pivot 时

    pivot (
    max(attribute) as max_value for attribute IN ( 'aaaaaa', 
                                                   'bbbbbb', 
                                                   'cccccc', 
                                                   'dddddd', 
                                                   'eeeeee'
                                      ))

我越来越接近我想要的,但对于 eeeee 我只得到一个值

material    |aaaaaa | bbbbbb | cccccc | dddddd | eeeeee |
__________________________________________________________
Material_A  |       | hello  | val_1  | 2      | 24     |

但我想要的是类似的东西

material    |aaaaaa | bbbbbb | cccccc | dddddd | eeeeee_1 | eeeeee_2 | eeeeee_3 |
   __________________________________________________________________________________
Material_A  |       | hello  | val_1  | 2      | 15            16    |     24

【问题讨论】:

我编辑了所有内容 :) 【参考方案1】:

如果eeeeee 总是有 3 个值,那么您可以按照以下方式进行操作

SQL> with mat_table (material, attribute, value) as
  2  (
  3  select 'Material_A', 'aaaaaa', null from dual
  4  union all select 'Material_A', 'bbbbbb', 'hello' from dual
  5  union all select 'Material_A', 'cccccc', 'val_1' from dual
  6  union all select 'Material_A', 'dddddd', '2' from dual
  7  union all select 'Material_A', 'eeeeee', '15' from dual
  8  union all select 'Material_A', 'eeeeee', '16' from dual
  9  union all select 'Material_A', 'eeeeee', '24' from dual
 10  )
 11  select *
 12    from (select t.*,
 13                 row_number() over(partition by attribute order by value) rn
 14            from mat_table t)
 15  pivot (max(value) for (attribute, rn) in
 16  (
 17   ('aaaaaa', 1), ('bbbbbb', 1), ('cccccc', 1), ('dddddd', 1),
 18   ('eeeeee', 1), ('eeeeee', 2), ('eeeeee', 3)
 19  ));

MATERIAL   'aaaa 'bbbb 'cccc 'dddd 'eeee 'eeee 'eeee
---------- ----- ----- ----- ----- ----- ----- -----
Material_A       hello val_1 2     15    16    24

但是,如果您希望 Oracle 为 eeeeee 的任意数量的值动态创建列,那么这是不可能的。

详细说明请看这里Oracle Dynamic Pivoting

您可以为attributevalue 的任意组合生成 XML,但如果您想使用 SQL 显示结果,那么最终必须指定所有列(另一种方法是在客户端解析 XML)。

SQL> with mat_table (material, attribute, value) as
  2  (
  3  select 'Material_A', 'aaaaaa', null from dual
  4  union all select 'Material_A', 'bbbbbb', 'hello' from dual
  5  union all select 'Material_A', 'cccccc', 'val_1' from dual
  6  union all select 'Material_A', 'dddddd', '2' from dual
  7  union all select 'Material_A', 'eeeeee', '15' from dual
  8  union all select 'Material_A', 'eeeeee', '16' from dual
  9  union all select 'Material_A', 'eeeeee', '24' from dual
 10  )
 11  select material, x.*
 12  from mat_table
 13  pivot xml (count(*) as dummy for (attribute, value) in (any, any))
 14  -- parsing output
 15  , xmltable('/PivotSet' passing attribute_value_xml
 16             columns
 17               aaaaaa varchar2(10) path '/PivotSet/item[column="aaaaaa"]/column[2]',
 18               bbbbbb varchar2(10) path '/PivotSet/item[column="bbbbbb"]/column[2]',
 19               cccccc varchar2(10) path '/PivotSet/item[column="cccccc"]/column[2]',
 20               dddddd varchar2(10) path '/PivotSet/item[column="dddddd"]/column[2]',
 21               eeeeee_1 varchar2(10) path '/PivotSet/item[column="eeeeee"][1]/column[2]',
 22               eeeeee_2 varchar2(10) path '/PivotSet/item[column="eeeeee"][2]/column[2]',
 23               eeeeee_3 varchar2(10) path '/PivotSet/item[column="eeeeee"][3]/column[2]') x;

MATERIAL   AAAAAA     BBBBBB     CCCCCC     DDDDDD     EEEEEE_1   EEEEEE_2   EEEEEE_3
---------- ---------- ---------- ---------- ---------- ---------- ---------- ----------
Material_A            hello      val_1      2          15         16         24

在这种情况下,不能保证 EEEEEE_1/EEEEEE_2/EEEEEE_3 将完全按照这个顺序在 15/16/24。

【讨论】:

我总是最多 3 个值。我试过你的代码,但我有 EEEEEE_1: 24 和 EEEEEE_2: null 和 EEEEEE_3 null 正如您在答案中看到的,代码根据起始帖子准确返回所需内容。如果你得到别的东西,那么你要么修改了代码,要么使用了一些没有考虑到的细节的数据。为什么不更新原始帖子并提供扩展示例?【参考方案2】:

编辑:TS 评论说它不适用于多种材料。所以我扩展了答案来解决这个问题。

您可以在第一个查询中简单地将 row_number(按材料、属性分区)连接到属性。如果您愿意,可以按值添加排序。为了考虑多种材料,row_number 也按材料分区。这意味着不同材质的相同属性将获得相同的名称并最终出现在同一列中。

attribute_table.attribute替换为

concat(attribute_table.attribute,'_', row_number() over (partition by attribute_table.material, attribute_table.attribute order by attribute_table.attribute, attribute_table.value))

完整的代码和结果:

with mat_table  as
  (
  select 'Material_A' as material, 'aaaaaa' as attribute, null as value
  union all select 'Material_A', 'bbbbbb', 'hello' 
  union all select 'Material_A', 'cccccc', 'val_1' 
  union all select 'Material_A', 'dddddd', '2' 
  union all select 'Material_A', 'eeeeee', '15' 
  union all select 'Material_A', 'eeeeee', '16' 
  union all select 'Material_A', 'eeeeee', '24' 
  union all select 'Material_B' , 'aaaaaa', 'lol' 
  union all select 'Material_B', 'bbbbbb', 'hi' 
  union all select 'Material_B', 'cccccc', 'max_val' 
  union all select 'Material_B', 'dddddd', '4' 
  union all select 'Material_B', 'eeeeee', '67' 
  union all select 'Material_B', 'eeeeee', '99' 
  union all select 'Material_B', 'eeeeee', null
  )

select *
from (
    select t.material,
            t.value ,
            concat(t.attribute,'_', row_number() over (partition by t.material , t.attribute order by t.attribute, t.value)) as numbered_attribute
    from mat_table t) as d
pivot (
    max(d.value)  
    for numbered_attribute IN ( [aaaaaa_1],  
                                [bbbbbb_1], 
                                [cccccc_1], 
                                [dddddd_1], 
                                [eeeeee_1],
                                [eeeeee_2],
                                [eeeeee_3]
                             )) as total
    order by total.material

注意:我使用的是 SQL-Server。也许您必须更改一些语法,例如 [eeeeee_3] => 'eeeeee_3'

带有编号属性的基表:

pivot 后的最终结果:

【讨论】:

实际问题恰恰是pivot过程,而不是列命名过程。 他拥有的代码做他想做的事,除了具有相同属性的行。这解决了他的那部分问题。 这可能不是技术上最干净的,但很容易理解,只需要对他当前的代码进行很小的改动。 谢谢它适用于我上面的例子,但如果我有不止一种材料,那就不行了。如果我有大约 100 行,我最终会得到尽可能多的列:/ 更新了针对不同材料的答案

以上是关于如何将具有重复值的行转换为列?的主要内容,如果未能解决你的问题,请参考以下文章

将具有动态日期的行转换为列

如何将具有排名值的列转换为oracle中的行

如何将 Bigquery 重复记录转换为列?

如何在sql中将行转换为列

如何将多个重复行转换为列,然后在记事本++中从列转换回行

将 Pandas DataFrame 的行转换为列标题,