【问题标题】:Oracle 11g split text column to rowsOracle 11g 将文本列拆分为行
【发布时间】:2011-09-20 06:13:53
【问题描述】:

我有桌子:

ID   |Values
-----+--------------------------------
1    |AB,AD
2    |AG, ... ,BD
3    |AV

如何将其转换为:

ID   |Value
-----+------
1    |AB
1    |AD
2    |AG
...  |...
2    |BD
3    |AV

【问题讨论】:

  • 我必须使用一个选择语句来完成
  • 糟糕的数据库设计,将值存储在分隔字符串中。建议您的应用程序设计人员(或 DBA)在将字符串插入数据库之前对其进行拆分。
  • @Ollie 和 downvoters:这种情况并不少见。这其实是个好问题
  • @LukasEder,这是一个很好的问题,但它仍然是糟糕的数据库设计。
  • 在看到全貌之前,我不会判断非规范化。在某些情况下,它有其原因......

标签: sql oracle oracle11g


【解决方案1】:

使用内置的 XML 函数,您可以这样做:

with sample_data as
(
 select 1 id, 'AB,AD' vals from dual union all
 select 2, 'AG,AK,AJ,BA,BD' from dual union all
 select 3, 'AV' from dual
)
select id, cast(t.column_value.extract('//text()') as varchar2(10)) val
  from sample_data,
  table( xmlsequence( xmltype(
    '<x><x>' || replace(vals, ',', '</x><x>') || '</x></x>'
  ).extract('//x/*'))) t;

结果:

ID   VAL
--- -----
 1   AB
 1   AD
 2   AG
 2   AK
 2   AJ
 2   BA
 2   BD
 3   AV

【讨论】:

    【解决方案2】:

    使用递归公用表表达式,相同的查询如下所示:

    with sample_data as
    (
      select 1 id, 'AB,AD' vals from dual union all
      select 2, 'AG,AK,AJ,BA,BD' from dual union all
      select 3, 'AV' from dual
    ),
    split_first(id, val, rem) as
    (
      select id,
        coalesce(substr(vals, 1, instr(vals, ',') - 1), vals) val,
        case when instr(vals, ',') > 0 then substr(vals, instr(vals, ',') + 1) end rem
      from sample_data
      union all
      select id,
        coalesce(substr(rem, 1, instr(rem, ',') - 1), rem) val,
        case when instr(rem, ',') > 0 then substr(rem, instr(rem, ',') + 1) end rem
      from split_first
      where rem is not null
    )
    select id, val from split_first
    order by id;
    

    或者稍微不同的方法:

    with sample_data as
    (
      select 1 id, 'AB,AD' vals from dual union all
      select 2, 'AG,AK,AJ,BA,BD' from dual union all
      select 3, 'AV' from dual
    ),
    pos(id, seq, vals, sta, stp) as
    (
      select id, 1, vals, 1, instr(vals, ',') from sample_data
      union all
      select id, seq + 1, vals, stp + 1, instr(vals, ',', stp + 1) from pos
      where stp > 0
    )
    select id, substr(vals, sta, case when stp > 0 then stp - sta else length(vals) end) from pos
    order by id, seq;
    

    【讨论】:

    • 哪种方案更高效?
    • 在 I/O 方面,它不应该有任何区别,因为处理的是相同的行。 CPU 明智的第二个查询可能更有效,因为它使用较少的字符串操作。
    猜你喜欢
    • 1970-01-01
    • 2011-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多