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 值行以查看元素的编号。
它假设数据不包含逗号。
我有一个 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 值行以查看元素的编号。
它假设数据不包含逗号。