oracle SQL:改进connect by子句

时间:2015-08-31 18:48:55

标签: oracle regexp-substr

我有一张桌子:Table_1如下所示:

id   |   column1
-----------------
10   |   abc, kdm
20   |   xyz, lop, nkk

我想要的是转换表如下所示:

id   |   column1
-----------------
10   |   abc
10   |   kdm
20   |   xyz 
20   |   lop
20   |   nkk

为此,我使用了这样的查询:

select id, regexp_substr(column1,'[^,]+', 1, level) from Table_1 
connect by regexp_substr(column1, '[^,]+', 1, level) is not null;

只要逗号分隔值的数量较少,此查询就可以正常工作。但是当它增长时,它会消耗越来越多的时间来处理。

我想到的一个解决方案是创建一个单独的表,然后通过迭代Table_1的值来插入值。

伪代码如下:

FOR r in each row
    FOR i in 1..length(comma_separated_values)
       insert into new_table values(id, select regexp_substr(column1,'[^,]+', 1, i) from Table_1 
    End LOOP;
End LOOP;

但是,由于逗号分隔值增长也会消耗很多时间,是否还有其他最佳方法(首选不使用其他表,但临时/虚拟表可以)?

我正在使用Oracle SQL。

提前致谢。

3 个答案:

答案 0 :(得分:1)

显然兰迪建议修复根本问题是理想的。如果不可能,那么可以使用各种选项。列出了许多here 。通常,虽然可以提高性能的简单解决方案是在column1中找到最大数量的值,创建包含许多列的临时表,然后将该临时表转换为所需的格式。即,有一个中间步骤,表格看起来像id|val1|val2|val3|..|valn

答案 1 :(得分:1)

规范化问题无法承受,您的原始查询实际上不起作用。在测试数据上运行时:

SQL> with Table_1(id, column1) as (
  2    select 10, 'abc, kdm' from dual
  3    union
  4    select 20, 'xyz, lop, nkk' from dual
  5  )
  6  select id, regexp_substr(column1,'[^,]+', 1, level) from Table_1
  7  connect by regexp_substr(column1, '[^,]+', 1, level) is not null;

        ID REGEXP_SUBSTR
---------- -------------
        10 abc
        10  kdm
        20  nkk
        20  lop
        20  nkk
        20 xyz
        10  kdm
        20  nkk
        20  lop
        20  nkk

10 rows selected.

SQL>

因此,当您添加更多值时,问题会呈指数级增长,从而降低性能。通过添加第三行进行测试。此外,您的分隔符是逗号空格,而不仅仅是逗号。 And the regex form used to parse the list ('[^,]+') does not work with nulls and should be avoided。不幸的是,它是最常见的正则表达式,你会看到它作为解析列表的答案。

试试这个,它应该轻松处理更大的列表:

SQL> with Table_1(id, column1) as (
     select 10, 'abc, kdm' from dual
     union
     select 20, 'xyz, lop, nkk' from dual
   )
   SELECT id, -- column1,
              --  COLUMN_VALUE AS match_nbr,
          REGEXP_SUBSTR( column1 ,'(.*?)(, |$)', 1, COLUMN_VALUE, NULL, 1 ) AS match_value
   FROM
     Table_1,
     TABLE(
       CAST(
         MULTISET(
           SELECT LEVEL
           FROM   DUAL
           CONNECT BY LEVEL <= REGEXP_COUNT(column1 , ',' )+1
         ) AS SYS.ODCINUMBERLIST
       )
     );

        ID MATCH_VALUE
---------- -------------
        10 abc
        10 kdm
        20 xyz
        20 lop
        20 nkk

SQL>

基本上,这会创建一个表,每行一行,列表中的元素数(包含元素编号)并将其与主表连接。
取消注释COLUMN_VALUE值行以查看元素的编号。 它假定数据不包含逗号。

答案 2 :(得分:0)

您可以尝试使用DBMS_UTILITY.COMMA_TO_TABLE程序,我猜它应该比用户定义的解决方案更快。

文档链接:http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_util.htm