Fork me on GitHub
余鸢

Oracle-拆分字符串

使用分层查询拆分字符串

示例数据:

1
2
3
4
5
CREATE TABLE table_name ( id, list ) AS
SELECT 1, 'a,b,c,d' FROM DUAL UNION ALL -- Multiple items in the list
SELECT 2, 'e' FROM DUAL UNION ALL -- Single item in the list
SELECT 3, NULL FROM DUAL UNION ALL -- NULL list
SELECT 4, 'f,,g' FROM DUAL; -- NULL item in the list

查询:

1
2
3
4
5
6
7
8
SELECT t.id,
REGEXP_SUBSTR( list, '([^,]*)(,|$)', 1, LEVEL, NULL, 1 ) AS value,
LEVEL AS lvl
FROM table_name t
CONNECT BY
id = PRIOR id
AND PRIOR SYS_GUID() IS NOT NULL
AND LEVEL < REGEXP_COUNT( list, '([^,]*)(,|$)' )

输出:

1
2
3
4
5
6
7
8
9
10
11
ID ITEM LVL
---------- ------- ----------
1 a 1
1 b 2
1 c 3
1 d 4
2 e 1
3 (NULL) 1
4 f 1
4 (NULL) 2
4 g 3

使用XMLTable拆分分隔字符串

示例数据:

1
2
3
4
5
CREATE TABLE table_name ( id, list ) AS
SELECT 1, 'a,b,c,d' FROM DUAL UNION ALL -- Multiple items in the list
SELECT 2, 'e' FROM DUAL UNION ALL -- Single item in the list
SELECT 3, NULL FROM DUAL UNION ALL -- NULL list
SELECT 4, 'f,,g' FROM DUAL; -- NULL item in the list

查询:

1
2
3
4
5
6
7
8
9
10
SELECT t.id,
SUBSTRING( x.item.getStringVal(), 2 ) AS item,
x.lvl
FROM table_name t
CROSS JOIN
XMLTABLE(
( '"#' || REPLACE( t.list, ',', '","#' ) || '"' )
COLUMNS item XMLTYPE PATH '.'
lvl FOR ORDINALITY
) x;

(注意:附加字符以便于提取NULL值;稍后使用SUBSTR( item, 2 )删除它)。如果不需要NULL值,那么可以简化查询并省略此操作。

输出:

1
2
3
4
5
6
7
8
9
10
11
ID ITEM LVL
---------- ------- ----------
1 a 1
1 b 2
1 c 3
1 d 4
2 e 1
3 (NULL) 1
4 f 1
4 (NULL) 2
4 g 3

使用相关表表达式拆分字符串

示例数据:

1
2
3
4
5
CREATE TABLE table_name ( id, list ) AS
SELECT 1, 'a,b,c,d' FROM DUAL UNION ALL -- Multiple items in the list
SELECT 2, 'e' FROM DUAL UNION ALL -- Single item in the list
SELECT 3, NULL FROM DUAL UNION ALL -- NULL list
SELECT 4, 'f,,g' FROM DUAL; -- NULL item in the list

查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
SELECT t.id,
v.COLUMN_VALUE AS value,
ROW_NUMBER() OVER ( PARTITION BY id ORDER BY ROWNUM ) AS lvl
FROM table_name t,
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.list, '([^,]*)(,|$)', 1, LEVEL, NULL, 1 )
FROM DUAL
CONNECT BY LEVEL < REGEXP_COUNT( t.list, '[^,]*(,|$)' )
)
AS SYS.ODCIVARCHAR2LIST
)
) v;

输出:

1
2
3
4
5
6
7
8
9
10
11
ID ITEM LVL
---------- ------- ----------
1 a 1
1 b 2
1 c 3
1 d 4
2 e 1
3 (NULL) 1
4 f 1
4 (NULL) 2
4 g 3

使用PL / SQL函数拆分字符串

PL / SQL函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
CREATE OR REPLACE FUNCTION split_String(
i_str IN VARCHAR2,
i_delim IN VARCHAR2 DEFAULT ','
) RETURN SYS.ODCIVARCHAR2LIST DETERMINISTIC
AS
p_result SYS.ODCIVARCHAR2LIST := SYS.ODCIVARCHAR2LIST();
p_start NUMBER(5) := 1;
p_end NUMBER(5);
c_len CONSTANT NUMBER(5) := LENGTH( i_str );
c_ld CONSTANT NUMBER(5) := LENGTH( i_delim );
BEGIN
IF c_len > 0 THEN
p_end := INSTR( i_str, i_delim, p_start );
WHILE p_end > 0 LOOP
p_result.EXTEND;
p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, p_end - p_start );
p_start := p_end + c_ld;
p_end := INSTR( i_str, i_delim, p_start );
END LOOP;
IF p_start <= c_len + 1 THEN
p_result.EXTEND;
p_result( p_result.COUNT ) := SUBSTR( i_str, p_start, c_len - p_start + 1 );
END IF;
END IF;
RETURN p_result;
END;
/

示例数据:

1
2
3
4
5
CREATE TABLE table_name ( id, list ) AS
SELECT 1, 'a,b,c,d' FROM DUAL UNION ALL -- Multiple items in the list
SELECT 2, 'e' FROM DUAL UNION ALL -- Single item in the list
SELECT 3, NULL FROM DUAL UNION ALL -- NULL list
SELECT 4, 'f,,g' FROM DUAL; -- NULL item in the list

查询:

1
2
3
4
5
SELECT t.id,
v.column_value AS value,
ROW_NUMBER() OVER ( PARTITION BY id ORDER BY ROWNUM ) AS lvl
FROM table_name t,
TABLE( split_String( t.list ) ) (+) v

输出:

1
2
3
4
5
6
7
8
9
10
11
ID ITEM LVL
---------- ------- ----------
1 a 1
1 b 2
1 c 3
1 d 4
2 e 1
3 (NULL) 1
4 f 1
4 (NULL) 2
4 g 3

使用递归子查询因子分解条件拆分字符串

示例数据:

1
2
3
4
5
CREATE TABLE table_name ( id, list ) AS
SELECT 1, 'a,b,c,d' FROM DUAL UNION ALL -- Multiple items in the list
SELECT 2, 'e' FROM DUAL UNION ALL -- Single item in the list
SELECT 3, NULL FROM DUAL UNION ALL -- NULL list
SELECT 4, 'f,,g' FROM DUAL; -- NULL item in the list

查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
WITH bounds ( id, list, start_pos, end_pos, lvl ) AS (
SELECT id, list, 1, INSTR( list, ',' ), 1 FROM table_name
UNION ALL
SELECT id,
list,
end_pos + 1,
INSTR( list, ',', end_pos + 1 ),
lvl + 1
FROM bounds
WHERE end_pos > 0
)
SELECT id,
SUBSTR(
list,
start_pos,
CASE end_pos
WHEN 0
THEN LENGTH( list ) + 1
ELSE end_pos
END - start_pos
) AS item,
lvl
FROM bounds
ORDER BY id, lvl;

输出:

1
2
3
4
5
6
7
8
9
10
11
ID ITEM LVL
---------- ------- ----------
1 a 1
1 b 2
1 c 3
1 d 4
2 e 1
3 (NULL) 1
4 f 1
4 (NULL) 2
4 g 3

使用CROSS APPLY拆分字符串(Oracle 12c)

示例数据:

1
2
3
4
5
CREATE TABLE table_name ( id, list ) AS
SELECT 1, 'a,b,c,d' FROM DUAL UNION ALL -- Multiple items in the list
SELECT 2, 'e' FROM DUAL UNION ALL -- Single item in the list
SELECT 3, NULL FROM DUAL UNION ALL -- NULL list
SELECT 4, 'f,,g' FROM DUAL; -- NULL item in the list

查询:

1
2
3
4
5
6
7
8
9
10
SELECT t.id,
REGEXP_SUBSTR( t.list, '([^,]*)($|,)', 1, l.lvl, NULL, 1 ) AS item,
l.lvl
FROM table_name t
CROSS APPLY
(
SELECT LEVEL AS lvl
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( t.list, ',' ) + 1
) l;

输出:

1
2
3
4
5
6
7
8
9
10
11
ID ITEM LVL
---------- ------- ----------
1 a 1
1 b 2
1 c 3
1 d 4
2 e 1
3 (NULL) 1
4 f 1
4 (NULL) 2
4 g 3

使用XMLTable和FLWOR表达式分割字符串

此解决方案使用Oracle 12提供的 ora:tokenize XQuery function

示例数据:

1
2
3
4
5
CREATE TABLE table_name ( id, list ) AS
SELECT 1, 'a,b,c,d' FROM DUAL UNION ALL -- Multiple items in the list
SELECT 2, 'e' FROM DUAL UNION ALL -- Single item in the list
SELECT 3, NULL FROM DUAL UNION ALL -- NULL list
SELECT 4, 'f,,g' FROM DUAL; -- NULL item in the list

查询:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
SELECT t.id,
x.item,
x.lvl
FROM table_name t,
XMLTABLE(
'let $list := tokenize(.,","),
$cnt := count($list)
for $val at $r in $list
where $r < $cnt
return $val'
PASSING list||','
COLUMNS
item VARCHAR2(100) PATH '.',
lvl FOR ORDINALITY
) x;

输出:

1
2
3
4
5
6
7
8
9
10
11
ID ITEM LVL
---------- ------- ----------
1 a 1
1 b 2
1 c 3
1 d 4
2 e 1
3 (NULL) 1
4 f 1
4 (NULL) 2
4 g 3