sql - 如何将逗号分隔的值转换为oracle中的行?

标签 sql oracle

这是 DDL——

create table tbl1 (
   id number,
   value varchar2(50)
);

insert into tbl1 values (1, 'AA, UT, BT, SK, SX');
insert into tbl1 values (2, 'AA, UT, SX');
insert into tbl1 values (3, 'UT, SK, SX, ZF');

注意,这里的值为 逗号分隔 字符串。

但是,我们需要如下结果-
ID VALUE
-------------
1  AA
1  UT
1  BT
1  SK
1  SX
2  AA
2  UT
2  SX
3  UT
3  SK
3  SX
3  ZF

我们如何为此编写 SQL?

最佳答案

我同意这是一个非常糟糕的设计。
如果您无法更改该设计,请尝试以下操作:

select distinct id, trim(regexp_substr(value,'[^,]+', 1, level) ) value, level
  from tbl1
   connect by regexp_substr(value, '[^,]+', 1, level) is not null
   order by id, level;

输出
id value level
1   AA  1
1   UT  2
1   BT  3
1   SK  4
1   SX  5
2   AA  1
2   UT  2
2   SX  3
3   UT  1
3   SK  2
3   SX  3
3   ZF  4

this

以更优雅和有效的方式删除重复项(感谢@mathguy)
select id, trim(regexp_substr(value,'[^,]+', 1, level) ) value, level
  from tbl1
   connect by regexp_substr(value, '[^,]+', 1, level) is not null
      and PRIOR id =  id 
      and PRIOR SYS_GUID() is not null  
   order by id, level;

如果您想要“ANSIer”方法,请使用 CTE:
with t (id,res,val,lev) as (
           select id, trim(regexp_substr(value,'[^,]+', 1, 1 )) res, value as val, 1 as lev
             from tbl1
            where regexp_substr(value, '[^,]+', 1, 1) is not null
            union all           
            select id, trim(regexp_substr(val,'[^,]+', 1, lev+1) ) res, val, lev+1 as lev
              from t
              where regexp_substr(val, '[^,]+', 1, lev+1) is not null
              )
select id, res,lev
  from t
order by id, lev;

输出
id  val lev
1   AA  1
1   UT  2
1   BT  3
1   SK  4
1   SX  5
2   AA  1
2   UT  2
2   SX  3
3   UT  1
3   SK  2
3   SX  3
3   ZF  4

MT0 的另一种递归方法,但没有正则表达式:
WITH t ( id, value, start_pos, end_pos ) AS
  ( SELECT id, value, 1, INSTR( value, ',' ) FROM tbl1
  UNION ALL
  SELECT id,
    value,
    end_pos                    + 1,
    INSTR( value, ',', end_pos + 1 )
  FROM t
  WHERE end_pos > 0
  )
SELECT id,
  SUBSTR( value, start_pos, DECODE( end_pos, 0, LENGTH( value ) + 1, end_pos ) - start_pos ) AS value
FROM t
ORDER BY id,
  start_pos;

我尝试了 3 种方法,使用 30000 行数据集和 118104 行返回,并得到以下平均结果:
  • 我的递归方法:5 秒
  • MT0 进场:4 秒
  • Mathguy 方法:16 秒
  • MT0 递归方法无正则表达式:3.45 秒

  • @Mathguy 还使用更大的数据集进行了测试:

    In all cases the recursive query (I only tested the one with regular substr and instr) does better, by a factor of 2 to 5. Here are the combinations of # of strings / tokens per string and CTAS execution times for hierarchical vs. recursive, hierarchical first. All times in seconds


  • 30,000 x 4:5/1。
  • 30,000 x 10:15/3。
  • 30,000 x 25:56/37。
  • 5,000 x 50:33/14。
  • 5,000 x 100:160/81。
  • 10,000 x 200:1,924/772
  • 关于sql - 如何将逗号分隔的值转换为oracle中的行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38371989/

    相关文章:

    oracle - Oracle 中的触发器可以传递参数吗?

    python - django 插入 Oracle 数据库无效标识符

    sql - Oracle SQL 触发器插入/更新

    sql - 来自SQL Server的更多有用的错误消息

    android sqlite 使用 where 子句创建索引失败

    ruby - 使用 Sequel 创建记录时出现“Sequel::Error: id is a restricted primary key”

    oracle - PL/SQL 中的 Double 'at' 符号含义

    oracle - PL/SQL 游标循环

    php - SQL 查询 : order by length of characters?

    php - 加速此代码 - PHP/SQL - 短代码但目前需要非常长的时间