@Littlefoot 给出的选项的详细解释
下面是多值字符串转换为列和行的测试用例。
set lines 999 pages 999
col ID for a20
col NAME for a20
variable B1 varchar2(60)
exec :B1:='(99,''TABLE1''),(56,''INDEX1''),(199,''TABLE''),(156,''INDEX'')';
variable B1 varchar2(100)
exec :B1:='(''TABL234E1~'',99),(''I1NDEX1~'',5ABC6),(''TAB1LE'',4ABC0),(''IND11EX'',6ACDE0)';
WITH
test (col)
AS
(SELECT :b1 FROM DUAL)
SELECT SUBSTR (str, 1, INSTR (str, ',') - 1) id,
SUBSTR (str, INSTR (str, ',') + 1) name
FROM ( SELECT REGEXP_SUBSTR (
REPLACE (
REPLACE (
REPLACE (REPLACE (col, '),(', '#'), CHR (39), ''),
'(',
''),
')',
''),
'[^#]+',
1,
LEVEL) str
FROM test
CONNECT BY LEVEL <= REGEXP_COUNT (REPLACE (col, '),(', '#'), '#') + 1);
解释:
variable B1 varchar2(60)
exec :B1:='(99,''TABLE1''),(56,''INDEX1''),(199,''TABLE''),(156,''INDEX'')';
步骤1
WITH
test (col)
AS
(SELECT :b1 FROM DUAL)
select col from test;
此步骤有助于获取 oracle 行格式的基本字符串。
结果:
(99,'TABLE1'),(56,'INDEX1'),(199,'TABLE'),(156,'INDEX')
第二步
WITH
test (col)
AS
(SELECT :b1 FROM DUAL)
select REPLACE(col, '),(','#') from test;
此步骤有助于将 '),(' 替换为 '#'(以获得更简单的多值字符串分隔符)。
结果:
(99,'TABLE1'#56,'INDEX1'#199,'TABLE'#156,'INDEX')
第三步
WITH
test (col)
AS
(SELECT :b1 FROM DUAL)
select REPLACE(REPLACE(col, '),(','#'),CHR(39),'') from test;
此步骤有助于替换上一步中的单引号。
结果:
(TABL234E1~,99#I1NDEX1~,5ABC6#TAB1LE,4ABC0#IND11EX,6ACDE0)
第四步
WITH
test (col)
AS
(SELECT :b1 FROM DUAL)
select REPLACE(REPLACE(REPLACE(col, '),(','#'),CHR(39),''),'(','') from test;
此步骤有助于将 '(' 替换为 ''(从前面的步骤中删除前导括号)。
结果:
99,TABLE1#56,INDEX1#199,TABLE#156,INDEX)
步骤5
WITH
test (col)
AS
(SELECT :b1 FROM DUAL)
select REPLACE(REPLACE(REPLACE(REPLACE(col, '),(','#'),CHR(39),''),'(',''),')','') from test;
此步骤有助于将 ')' 替换为 ''(从前面的步骤中删除尾括号)。
结果:
99,TABLE1#56,INDEX1#199,TABLE#156,INDEX
步骤6
WITH
test (col)
AS
(SELECT :b1 FROM DUAL)
SELECT *
FROM ( SELECT REGEXP_SUBSTR (
REPLACE (
REPLACE (
REPLACE (REPLACE (col, '),(', '#'), CHR (39), ''),
'(',
''),
')',
''),
'[^#]+',
1,
LEVEL) str
FROM test
CONNECT BY LEVEL <= REGEXP_COUNT (REPLACE (col, '),(', '#'), '#') + 1);
这一步有两个部分:
a. REGEXP_SUBSTR(<value from previous step>,'[^#]+',1,LEVEL)
'[^#]+' ==> searches and gets every character other than '#'
1==> starting position of the searches
LEVEL==> LEVEL can be used in conjunction with CONNECT BY LEVEL clause. All the chunks of the source string can be displayed by using the LEVEL keyword as the match occurrence.
b. CONNECT BY LEVEL <= REGEXP_COUNT (REPLACE (col, '),(', '#'), '#') + 1
REPLACE (col, '),(', '#') ==> This helps in replacing '),(' with '#' (to get a simpler separator for multivalued string).
REGEXP_COUNT(<output from previous step>,'#')+1==> Count the number of '#' +1 in the source string.
Here, the CONNECT BY LEVEL clause generates the rows equal to the number of '#' +1 in the source string.
结果:
99,TABLE1
56,INDEX1
199,TABLE
156,INDEX
步骤7
WITH
test (col)
AS
(SELECT :b1 FROM DUAL)
SELECT SUBSTR (str, 1, INSTR (str, ',') - 1) id,
SUBSTR (str, INSTR (str, ',') + 1) name
FROM ( SELECT REGEXP_SUBSTR (
REPLACE (
REPLACE (
REPLACE (REPLACE (col, '),(', '#'), CHR (39), ''),
'(',
''),
')',
''),
'[^#]+',
1,
LEVEL) str
FROM test
CONNECT BY LEVEL <= REGEXP_COUNT (REPLACE (col, '),(', '#'), '#') + 1);
这一步有 2 个组成部分:
a.SUBSTR (str, 1, INSTR (str, ',') - 1)==> str 是从前面的输出中提取的,从第一个位置开始,它将字符从 ',' greps 到 -1 位置。
b.SUBSTR (str, INSTR (str, ',') + 1) name==> str 是从前面的输出中提取的,从 ',' 开始,它将字符从 ',' 到 +1 位置。
结果:
ID NAME
-------------------- --------------------
99 TABLE1
56 INDEX1
199 TABLE
156 INDEX