UNPIVOT 多行分隔字符串的结果

Posted

技术标签:

【中文标题】UNPIVOT 多行分隔字符串的结果【英文标题】:UNPIVOT the results of a delimited string on several rows 【发布时间】:2013-08-22 14:09:50 【问题描述】:

我正在努力处理一些复杂的分层数据。我已成功使用 CONNECT BY 查询将行限制为我想要的子集 - 我已使用 SYS_CONNECT_BY_PATH 将完整树返回到感兴趣的节点。

这基本上给了我一些这样的行(由'|'分隔):

id  path
-------------------
1, '|10|11|12|13'
2, '|10|14|15'
3, '|16|11|12|13'
4, '|16|17'

现在 - 我的挑战是将这些值解包或 UNPIVOT 重新转换为这样的结构:

id  ord node
-------------
1, 1, 10
1, 2, 11
1, 3, 12
1, 4, 13
2, 1, 10
2, 2, 14
2, 3, 15
3, 1, 16
3, 2, 11
3, 3, 12
3, 4, 13
4, 1, 16
4, 2, 17

我认为我无法直接使用 UNPIVOT,因为它正在处理一组固定的列 - 这不是。

我正在使用 PIPELINE 函数来解包,但坦率地说 - 将所有这些行传递给函数是一个问题,因为它们来自另一个查询。我想知道是否有人有办法将 SYS_CONNECT_BY_PATH 结果集中的值恢复为可能是纯 sql 解决方案的行 - 可能使用 REGEX 解析...

感谢您的帮助 - 谢谢

【问题讨论】:

为什么要连接它们而不是直接存储值? 连接来自 SYS_CONNECT_BY_PATH 的结果 - 在原始数据库中,值都被正确规范化到层次结构中 【参考方案1】:

是的,UNPIVOT 操作员在这里不会做很多事情来帮助您产生所需的输出。

作为一种方法,您可以使用regexp_count()(11g R1 版本及更高版本)常规

表达式函数计算所有出现的数字,然后使用regexp_substr()正则

提取数字的表达式函数如下:

-- sample of data 
SQL> with t1(id1, path1) as(
  2    select 1, '|10|11|12|13' from dual union all
  3    select 2, '|10|14|15' from dual union all
  4    select 3, '|16|11|12|13' from dual union all
  5    select 4, '|16|17' from dual
  6  ),
  7  occurrences(ocr) as( -- occurrences 
  8    select level
  9     from ( select max(regexp_count(path1, '[^|]+')) as mx_ocr
 10              from t1
 11          ) t
 12   connect by level <= t.mx_ocr
 13  )
 14  select id1
 15       , row_number() over(partition by id1 order by id1) as ord
 16       , node
 17    from ( select q.id1
 18                , regexp_substr(q.path1, '[^|]+', 1, o.ocr)        as node
 19             from t1 q
 20            cross join occurrences o
 21          )
 22  where  node is not null
 23  order by id1, 2, node
 24  ;

结果:

       ID1        ORD NODE
---------- ---------- ------------------------------------------------
         1          1 10
         1          2 11
         1          3 12
         1          4 13
         2          1 10
         2          2 14
         2          3 15
         3          1 11
         3          2 12
         3          3 13
         3          4 16
         4          1 16
         4          2 17

13 rows selected

作为另一种方法,从 10g 版本及更高版本开始,您可以使用 model 子句:

 SQL> with t1(id1, path1) as(
  2    select 1, '|10|11|12|13' from dual union all
  3    select 2, '|10|14|15' from dual union all
  4    select 3, '|16|11|12|13' from dual union all
  5    select 4, '|16|17' from dual
  6  )
  7  select id1
  8       , ord
  9       , node
 10    from t1
 11   model
 12   partition by ( rownum as id1)
 13   dimension by ( 1 as ord)
 14   measures( path1
 15           , cast(null as varchar2(11)) as node
 16           , nvl(regexp_count(path1, '[^|]+'), 0) as ocr )
 17   rules(
 18      node[for ord from 1 to ocr[1] increment 1] = 
 19          regexp_substr(path1[1], '[^|]+', 1, cv(ord))
 20  )
 21  order by id1, ord, node
 22  ;

结果:

       ID1        ORD NODE
---------- ---------- -----------
         1          1 10
         1          2 11
         1          3 12
         1          4 13
         2          1 10
         2          2 14
         2          3 15
         3          1 16
         3          2 11
         3          3 12
         3          4 13
         4          1 16
         4          2 17

13 rows selected

SQLFiddle Demo

【讨论】:

我看到订单有点偏离......在 id = 3 中,节点 16 显示在最后而不是第一个......我会玩

以上是关于UNPIVOT 多行分隔字符串的结果的主要内容,如果未能解决你的问题,请参考以下文章

使用 SQL Server 将逗号分隔的文本转换为多列结果

oracle列转行函数

跨多行拆分可变长度分隔字符串(SQL)

将多行字符串转换为单个逗号分隔

Postgres:将单行转换为多行(unpivot)

如何使用横向视图将分隔字符串拆分为 Hive 中的多行