Oracle 無內建 SPLIT_STRING 回傳表型別,需自建 Nested Table 型別 + PIPELINED Table Function 實現「一行變多行」分割邏輯。本文提供完整型別定義、函數實作 (支援多字元分隔符、NULL 分隔符逐字分割)、調用方式 (TABLE() 轉換) 與效能說明。
CREATE OR REPLACE TYPE split_str IS TABLE OF VARCHAR2(100);
/
-- 此為 Nested Table 型別,可用於 TABLE() 操作符轉換為關聯式結果集
CREATE OR REPLACE FUNCTION fn_split (
p_str VARCHAR2,
p_delimiter VARCHAR2
) RETURN split_str PIPELINED AS
v_str VARCHAR2(4000) := p_str;
v_idx NUMBER;
v_sublen NUMBER;
BEGIN
-- 分隔符為 NULL:逐字元分割
IF p_delimiter IS NULL THEN
FOR x IN 1 .. LENGTH(v_str) LOOP
PIPE ROW(SUBSTR(v_str, x, 1));
END LOOP;
RETURN;
END IF;
-- 一般分割邏輯
v_idx := INSTR(v_str, p_delimiter);
WHILE v_idx <> 0 LOOP
PIPE ROW(SUBSTR(v_str, 1, v_idx - 1));
v_sublen := LENGTH(v_str) - (v_idx + LENGTH(p_delimiter) - 1);
v_str := SUBSTR(v_str, -v_sublen, v_sublen);
v_idx := INSTR(v_str, p_delimiter);
END LOOP;
PIPE ROW(v_str); -- 最後一段
RETURN;
END;
/
-- 方式 1:標量子查詢 (單行單欄回傳集合物件,較少用)
SELECT fn_split('This@@is@@it', '@@') FROM dual;
-- 方式 2:TABLE() 轉換為關聯式結果集 (標準用法,推薦)
SELECT * FROM TABLE(fn_split('This@@is@@it', '@@'));
-- 結果:
-- COLUMN_VALUE
-- ------------
-- This
-- is
-- it
| 需求 | 修改方向 |
|---|---|
| 回傳含序號 (1,2,3...) | 型別改為 TYPE split_rec IS RECORD (seq NUMBER, val VARCHAR2(100)),集合型別 TYPE split_tab IS TABLE OF split_rec |
| 支援 CLOB 超長字串 | 參數改 CLOB,邏輯用 DBMS_LOB.INSTR/SUBSTR |
| 正則表達式分割 | 11g+ 可用 REGEXP_SUBSTR + CONNECT BY LEVEL 純 SQL 實現,無需 PL/SQL |
| 效能優化 | 大量分割場景考慮 DETERMINISTIC、RESULT_CACHE 或改寫為純 SQL |
WITH data AS (SELECT 'This@@is@@it' AS str FROM dual)
SELECT REGEXP_SUBSTR(str, '[^@@]+', 1, LEVEL) AS token
FROM data
CONNECT BY REGEXP_SUBSTR(str, '[^@@]+', 1, LEVEL) IS NOT NULL;
優點:無物件依賴、優化器可推演、適合即席查詢。缺點:極大資料量時正則效能略低於 PL/SQL 迴圈。
PIPELINED 函數**逐行產出**,記憶體佔用低,適合大量資料流式處理。EXECUTE ON split_str。