oracle SQL:改进 connect by 子句

oracle SQL: improve connect by clause

我有一个 table:Table_1 如下所示:

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

我想要的是将 table 转换为如下所示:

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,然后通过遍历 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;

但是随着逗号分隔值的增长,这也会消耗很多时间,有没有其他最佳方法来做到这一点(最好不要使用另一个 table,而是 temporary/virtual table 可以吗)?

我正在使用 Oracle SQL。

提前致谢。

您可以为此尝试 DBMS_UTILITY.COMMA_TO_TABLE 程序,我想用户定义的解决方案应该更快。

Link 用于文档:http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_util.htm

显然,Randy 提出的解决根本问题的建议是最理想的。如果那不可能,那么可以使用多种选择。列出了很多 here .一般来说,虽然可以提高性能的一个简单解决方案是在 column1 中找到最大数量的值,创建一个包含那么多列的临时文件 table,然后将该临时文件 table 转换为您想要的格式。即,有一个中间步骤,其中 table 看起来像 id|val1|val2|val3|..|valn

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

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>

因此,当您添加更多值时,问题会呈指数增长,因此您的性能会下降。通过添加第三行进行测试。另外,您的分隔符是逗号-space,而不仅仅是逗号。 。不幸的是,这是最常见的正则表达式,您会看到它作为解析列表的答案。

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

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>

基本上,这使得列表中的元素数量(包含元素编号)成为 table,每行一行,并将其与主 table.
取消注释 COLUMN_VALUE 值行以查看元素的编号。 它假设数据不包含逗号。