在oracle中将一列数据拆分为多列

Posted

技术标签:

【中文标题】在oracle中将一列数据拆分为多列【英文标题】:Split one column data into multiple columns in oracle 【发布时间】:2018-05-08 10:53:34 【问题描述】:

在我的 oracle 查询中,我使用如下方式检索记录,结果如下所示 -

SELECT columnC
     , LISTAGG(r.columnA,',') WITHIN GROUP (ORDER BY r.columnB) AS Test_sensor
  FROM tableA
 GROUP BY columnC

目前的输出如下 -

ColumnC  |  Test_Sensor
=============================
Z12345   |  20,30,40,50,60,70

但我希望这些数据显示如下 -

ColumnC  |  Test_Sensor1 |  Test_Sensor2 |   Test_Sensor3  |  Test_Sensor4
==========================================================================
Z12345   |  20           |   30          |    40           |  50   

请帮我解决这个问题

谢谢 克兰蒂 RTR

【问题讨论】:

你知道总是有 6 个测试传感器列吗?在任何情况下,您想要做的就是所谓的“旋转”。如果列数变化,就是动态pivot,需要使用pl/sql来获取结果。 嗨,Gordan,感谢您的回复,能否请您告诉我枢轴语法,例如如何使用 LISTAGG 放置它 【参考方案1】:

您可以使用PIVOT(并且不需要使用LISTAGG):

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE TableA ( ColumnA, ColumnB, ColumnC ) AS
  SELECT 20, 'A', 'Z12345' FROM DUAL UNION ALL
  SELECT 30, 'B', 'Z12345' FROM DUAL UNION ALL
  SELECT 40, 'C', 'Z12345' FROM DUAL UNION ALL
  SELECT 50, 'D', 'Z12345' FROM DUAL UNION ALL
  SELECT 60, 'E', 'Z12345' FROM DUAL UNION ALL
  SELECT 70, 'F', 'Z12345' FROM DUAL;

查询 1

SELECT *
from (
  SELECT columnA,
         columnC,
         ROW_NUMBER() OVER ( PARTITION BY columnC ORDER BY columnB ) AS rn
  FROM   tableA
) a
PIVOT ( MAX( columnA ) FOR rn IN (
  1 AS test_sensor1,
  2 AS test_sensor2,
  3 AS test_sensor3,
  4 AS test_sensor4
) )

Results

| COLUMNC | TEST_SENSOR1 | TEST_SENSOR2 | TEST_SENSOR3 | TEST_SENSOR4 |
|---------|--------------|--------------|--------------|--------------|
|  Z12345 |           20 |           30 |           40 |           50 |

查询 2

您可以使用LISTAGG 来做到这一点,但它比使用PIVOT 效率低得多:

SELECT ColumnC,
       REGEXP_SUBSTR( test_sensor, '[^,]+', 1, 1 ) AS test_sensor1,
       REGEXP_SUBSTR( test_sensor, '[^,]+', 1, 2 ) AS test_sensor2,
       REGEXP_SUBSTR( test_sensor, '[^,]+', 1, 3 ) AS test_sensor3,
       REGEXP_SUBSTR( test_sensor, '[^,]+', 1, 4 ) AS test_sensor4
FROM   (
  SELECT ColumnC,
         LISTAGG( ColumnA, ',' ) WITHIN GROUP ( ORDER BY ColumnB )
           AS test_sensor
  FROM   TableA
  GROUP BY ColumnC
)

Results

| COLUMNC | TEST_SENSOR1 | TEST_SENSOR2 | TEST_SENSOR3 | TEST_SENSOR4 |
|---------|--------------|--------------|--------------|--------------|
|  Z12345 |           20 |           30 |           40 |           50 |

【讨论】:

感谢 MT0,我使用 LISTAGG 将 6 行数据显示到一个列单元格中。我想在只有 LISTAGG 可用时实现拆分 @KranthiRtr “当LISTAGG 仅可用时”是什么意思?如果您想要额外的列,只需将它们添加到PIVOTFOR 子句中。 我的意思是用LISTAGG不能实现吗? @KranthiRtr 你可以,但我不会,因为它的性能要差得多。 @KranthiRtr 你还没有描述你想用SUBSTR 做什么,我不会不断更新答案以应对不断增长的不断变化的需求列表。如果您想使用SUBSTR,请阅读文档。【参考方案2】:

如果您使用 LISTAGG,则需要注意 LISTAGG 会忽略 NULL 值。你确定你的 columnA 总是有数据吗?例如,如果 test_sensor2 为 NULL,则 listagg 操作的输出将为 20,40,50,60,70,因此在 NULL 值之后,所有传感器的数据都将报告错误!使用这个来纠正它:

replace(LISTAGG( nvl(to_char(ColumnA), ','), ',' ) 
        WITHIN GROUP ( ORDER BY ColumnB ), ',,,',',,')

现在你的输出是 20,,40,50,60,70,保持 test_sensor2 的 NULL 值和其余的在正确的位置。

但是现在还有另一个问题是'[^,]+' 形式的正则表达式会导致同样的问题!因此,即使 listagg 输出是固定的,解析后的输出在具有 NULL 值的列之后又回到关闭状态!下面显示了一种不同的形式来解决此问题。这是一个设置示例,因此您可以评论/取消评论以查看处理数据时的差异。总是期待意外!

with TableA ( ColumnA, ColumnB, ColumnC ) AS (
  SELECT 20, 'A', 'Z12345' FROM DUAL UNION ALL
  SELECT NULL, 'B', 'Z12345' FROM DUAL UNION ALL -- make NULL
  SELECT 40, 'C', 'Z12345' FROM DUAL UNION ALL
  SELECT 50, 'D', 'Z12345' FROM DUAL UNION ALL
  SELECT 60, 'E', 'Z12345' FROM DUAL UNION ALL
  SELECT 70, 'F', 'Z12345' FROM DUAL
),
tbl_tmp as (
  SELECT ColumnC,
         -- Preserve the NULL in position 2
         replace(LISTAGG( nvl(to_char(ColumnA), ','), ',' ) 
                 WITHIN GROUP ( ORDER BY ColumnB ), ',,,',',,')
           AS test_sensor
  FROM   TableA
  GROUP BY ColumnC
)
--select * from tbl_tmp;
-- regex of format [^,]+ does not handle NULLs
SELECT ColumnC,
--       REGEXP_SUBSTR( test_sensor, '[^,]+', 1, 1) AS test_sensor1,  
--       REGEXP_SUBSTR( test_sensor, '[^,]+', 1, 2 ) AS test_sensor2,
--       REGEXP_SUBSTR( test_sensor, '[^,]+', 1, 3 ) AS test_sensor3,
--       REGEXP_SUBSTR( test_sensor, '[^,]+', 1, 4 ) AS test_sensor4
       REGEXP_SUBSTR( test_sensor, '(.*?)(,|$)', 1, 1, NULL, 1) AS test_sensor1,
       REGEXP_SUBSTR( test_sensor, '(.*?)(,|$)', 1, 2, NULL, 1 ) AS test_sensor2,
       REGEXP_SUBSTR( test_sensor, '(.*?)(,|$)', 1, 3, NULL, 1 ) AS test_sensor3,
       REGEXP_SUBSTR( test_sensor, '(.*?)(,|$)', 1, 4, NULL, 1 ) AS test_sensor4
FROM  tbl_tmp;

【讨论】:

以上是关于在oracle中将一列数据拆分为多列的主要内容,如果未能解决你的问题,请参考以下文章

关于Oracle中实现单列拆分成多列的技术应用

在 PowerShell 中将字符串拆分为多列

在SQL过程中将一列中的逗号分隔值拆分为多列

如何将oracle 中一条数据拆分成多条

在熊猫数据框中将多列拆分为行

Spark SQL一列拆分多列