如何将具有重复值的行转换为列?
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
您可以为attribute
和value
的任意组合生成 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 行,我最终会得到尽可能多的列:/ 更新了针对不同材料的答案以上是关于如何将具有重复值的行转换为列?的主要内容,如果未能解决你的问题,请参考以下文章