利用 Oracle 階層式查詢 CONNECT BY LEVEL 產生序號,配合 INSTR 定位分隔符位置,SUBSTR 擷取子字串,將單欄位逗點分隔值拆分為多列。
SELECT SUBSTR(T.RPT_ID,
INSTR(T.RPT_ID, ',', 1, C.LV) + 1,
INSTR(T.RPT_ID, ',', 1, C.LV + 1) - INSTR(T.RPT_ID, ',', 1, C.LV) - 1
) AS RPT_ID
FROM (
SELECT ',' || '85,86,87' || ',' AS RPT_ID,
LENGTH('85,86,87' || ',') - NVL(LENGTH(REPLACE('85,86,87', ',')), 0) AS CNT
FROM DUAL
) T,
(
SELECT LEVEL LV
FROM DUAL
CONNECT BY LEVEL <= LENGTH('85,86,87' || ',') - NVL(LENGTH(REPLACE('85,86,87', ',')), 0)
) C
WHERE T.CNT >= C.LV;
',' || str || ',' 統一處理首尾元素LENGTH(str||',') - NVL(LENGTH(REPLACE(str, ',')), 0)CONNECT BY LEVEL <= 元素個數INSTR(str, ',', 1, N)-- 1. 先查最大元素數(用於 CONNECT BY 上限)
SELECT MAX(LENGTH(RPT_ID || ',') - NVL(LENGTH(REPLACE(RPT_ID, ',')), 0))
INTO v_max_cnt
FROM DIM_AUDIT_TABLE@sjmh_inter;
-- 2. 正式拆分查詢
SELECT T.T_NAME, T.T_NAME_COMM, T.T_COLUMN, T.T_COLUMN_COMM,
T.COMMENT_NL, T.COMMENT_NL_TIME, T.SEQ_USER_ID,
SUBSTR(T.RPT_ID,
INSTR(T.RPT_ID, ',', 1, C.LV) + 1,
INSTR(T.RPT_ID, ',', 1, C.LV + 1) - INSTR(T.RPT_ID, ',', 1, C.LV) - 1
) AS RPT_ID
FROM (
SELECT A.T_NAME, A.T_NAME_COMM, A.T_COLUMN, A.T_COLUMN_COMM,
A.COMMENT_NL, A.COMMENT_NL_TIME, A.SEQ_USER_ID,
',' || A.RPT_ID || ',' AS RPT_ID,
LENGTH(A.RPT_ID || ',') - NVL(LENGTH(REPLACE(A.RPT_ID, ',')), 0) AS CNT
FROM DIM_AUDIT_TABLE@sjmh_inter A
WHERE A.COMMENT_NL IS NOT NULL
) T,
(
SELECT LEVEL LV FROM DUAL CONNECT BY LEVEL <= v_max_cnt
) C
WHERE C.LV <= T.CNT;
若使用 Database Link 查詢遠端表,CONNECT BY LEVEL 的 DUAL 可能導致 只回傳第一列。解決方法:改用 ALL_OBJECTS 或 USER_OBJECTS 代替 DUAL:
SELECT LEVEL LV FROM ALL_OBJECTS CONNECT BY LEVEL <= v_max_cnt
SELECT TRIM(COLUMN_VALUE) AS RPT_ID
FROM XMLTABLE(('"' || REPLACE('85,86,87', ',', '","') || '"'));
SELECT REGEXP_SUBSTR('85,86,87', '[^,]+', 1, LEVEL) AS RPT_ID
FROM DUAL
CONNECT BY REGEXP_SUBSTR('85,86,87', '[^,]+', 1, LEVEL) IS NOT NULL;
| 方法 | 版本需求 | 效能 | 適用場景 |
|---|---|---|---|
| INSTR/SUBSTR + CONNECT BY | 8i+ | 中等 | 所有版本通用、大資料量 |
| XMLTABLE | 10g+ | 較慢 | 簡單字串、XML 整合場景 |
| REGEXP_SUBSTR | 10g+ | 較慢 | 複雜分隔符模式 |
| JSON_TABLE | 12c+ | 快 | JSON 格式資料 |