SQL - 一列的反透视结果

Posted

技术标签:

【中文标题】SQL - 一列的反透视结果【英文标题】:SQL - Unpivot result of one column 【发布时间】:2011-10-18 11:12:52 【问题描述】:

Oracle 10g,说我有这个以下列:

col
------------------------------------------------------------------------------------------------
[1,98]([1,81]([6,100828],[6,101260]),[1,81]([6,100529],[6,101259]),[1,81]([6,101709],[6,100474]))

我想显示这个结果:

col
------
100828
101260
100529
101259
101709
100474

是否可以通过 SQL 查询显示此结果?

实际上我尝试了什么:

SELECT SUBSTR(col, INSTR(col, ',', 1, 3) + 1, 6) exp_1,
       SUBSTR(col, INSTR(col, ',', 1, 5) + 1, 6) exp_2,
       SUBSTR(col, INSTR(col, ',', 1, 8) + 1, 6) exp_3,
       SUBSTR(col, INSTR(col, ',', 1, 10) + 1, 6) exp_4,
       SUBSTR(col, INSTR(col, ',', 1, 13) + 1, 6) exp_5,
       SUBSTR(col, INSTR(col, ',', 1, 15) + 1, 6) exp_6
  FROM (SELECT '[1,98]([1,81]([6,100828],[6,101260]),[1,81]([6,100529],[6,101259]),[1,81]([6,101709],[6,100474]))' col
          FROM dual) ; 

EXP_1  EXP_2  EXP_3  EXP_4  EXP_5  EXP_6
------ ------ ------ ------ ------ ------
100828 101260 100529 101259 101709 100474

但是,返回的 exp_% 的数量可以是可变的,并且总是成对的,这意味着另一行可以返回 8 个 exp_% :

SUBSTR(col, INSTR(col, ',', 1, 18) + 1, 6) exp_7 ,
SUBSTR(col, INSTR(col, ',', 1, 20) + 1, 6) exp_8

在修复 exp_% 的数量时提出建议也非常受欢迎!

谢谢。

【问题讨论】:

【参考方案1】:

假设您的表名为“foo”,列名为“col”:

with q as (
    select ','||regexp_replace(
        regexp_replace(
            regexp_replace(
                regexp_replace(col, '[[0-9,]*]\(', ''), 
                    '\[[0-9],', ''), 
                '[])]', ','), 
        ',,+', 
        ',') a from foo
) 
select data 
  from (select substr(a, instr(a, ',', 1, rownum) + 1, 6) data 
          from q,
               (select 1 from q connect by level < length(regexp_replace(a, '[0-9]', '')))
       )
;

这里是解释。这很快就会变得复杂,并且可能无法很好地扩展,所以买家要小心。

首先我想去掉 '[1,98](' 字段。

  1  with q as (
  2  select
  3          regexp_replace(col, '[[0-9,]*]\(', '')
  4  from foo
  5  )
  6* select * from q

REGEXP_REPLACE(COL,'[[0-9,]*]\(','')
------------------------------------------------------------------------------------------------------------------------------------
[6,100828],[6,101260])[6,100529],[6,101259])[6,101709],[6,100474]))

接下来我想去掉字段的“[n,”部分。

  1  with q as (
  2  select
  3      regexp_replace(
  4          regexp_replace(col, '[[0-9,]*]\(', ''),
  5          '\[[0-9],', ''
  6      ) a from foo
  7  )
  8* select * from q

A
------------------------------------------------------------------------------------------------------------------------------------
100828],101260])100529],101259])101709],100474]))

现在去掉所有的 ']' 和 ')'

  1  with q as (
  2  select
  3      regexp_replace(
  4      regexp_replace(
  5          regexp_replace(col, '[[0-9,]*]\(', ''),
  6          '\[[0-9],', ''),
  7          '[])]', ',')
  8       a from foo
  9  )
 10* select * from q

A
------------------------------------------------------------------------------------------------------------------------------------
100828,,101260,,100529,,101259,,101709,,100474,,,

去掉重复的逗号并在前面加上逗号。

  1  with q as (
  2  select ','||regexp_replace(
  3      regexp_replace(
  4      regexp_replace(
  5          regexp_replace(col, '[[0-9,]*]\(', ''),
  6          '\[[0-9],', ''),
  7          '[])]', ','),
  8      ',,+',
  9      ',') a from foo
 10  )
 11* select * from q

A
------------------------------------------------------------------------------------------------------------------------------------
,100828,101260,100529,101259,101709,100474,

算出它们有多少个字段并为每个字段创建一行。

  1  with q as (
  2  select ','||regexp_replace(
  3      regexp_replace(
  4      regexp_replace(
  5          regexp_replace(col, '[[0-9,]*]\(', ''),
  6          '\[[0-9],', ''),
  7          '[])]', ','),
  8      ',,+',
  9      ',') a from foo
 10  )
 11* select 1 from q connect by level < length(regexp_replace(a, '[0-9]', ''))

     1
----------
     1
     1
     1
     1
     1
     1
     1

使用 q 进行笛卡尔连接(请注意,如果您的表中有不止一行,这将不起作用。)和一个子字符串以获得最终答案。

  1  with q as (
  2  select ','||regexp_replace(
  3      regexp_replace(
  4      regexp_replace(
  5          regexp_replace(col, '[[0-9,]*]\(', ''),
  6          '\[[0-9],', ''),
  7          '[])]', ','),
  8      ',,+',
  9      ',') a from foo
 10  )
 11  select data
 12    from (select substr(a, instr(a, ',', 1, rownum) + 1, 6) data
 13        from q,
 14         (select 1 from q connect by level < length(regexp_replace(a, '[0-9]', '')))
 15*        )

DATA
------
100828
101260
100529
101259
101709
100474


6 rows selected.

【讨论】:

请注意,这在 11g 中使用 pivot/unpivot 可能会容易得多 谢谢理查德!这是相当令人印象深刻的,我还是有点迷茫,想问你它也有可能得到每对的结果,意味着每行两个,在我们的示例中为 100828,101260 和第二行 100529,101259 等...跨度> 应该是可以的。如果我有时间,我会看看我是否能想出办法。 谢谢,我添加了串联 ||使用 substr(a, instr(a, ',', 1, rownum) + 8, 6) 和 row_number() 并在 where 子句中仅获取奇数 WHERE MOD(rn, 2) 0,它可以工作现在很好,再次感谢您的帮助!

以上是关于SQL - 一列的反透视结果的主要内容,如果未能解决你的问题,请参考以下文章

多个类似列的反透视问题

Access SQL 中的反透视

根据分隔符拆分列,然后在保留其他列的情况下取消透视结果

SQL Server 中的反透视表

Databricks/Spark SQL 中的反透视表

PostgreSQL 中的反透视表