Oracle 內部有兩大執行引擎:
兩引擎切換會產生 CPU 額外開銷。FORALL/BULK COLLECT 等集合操作可減少切換,大幅提升效能。
TA_CUSTOMER 約 2 億筆資料。BIRTHDAY(多為 NULL)、CERTIFICATE_TYPE、CERTIFICATE_NO。CERTIFICATE_TYPE='0' 代表身分證,但錄入錯誤導致非 '0' 也可能是身分證,反之亦然。-- 基準:單純字串連接
SELECT COUNT(t.certificate_type || t.certificate_no || t.birthday)
FROM ta_customer t WHERE ROWNUM < 2000000;
-- 版本 1:逐字元 ASCII 檢查
SELECT COUNT(f_extract_birthday_v1(t.certificate_type, t.certificate_no, t.birthday))
FROM ta_customer t WHERE ROWNUM < 2000000;
-- 版本 2:CAST 數值轉換 + 例外處理
SELECT COUNT(f_extract_birthday_v2(t.certificate_type, t.certificate_no, t.birthday))
FROM ta_customer t WHERE ROWNUM < 2000000;
| 測試項目 | 200 萬筆 | 100 萬筆 | 倍率 |
|---|---|---|---|
| 字串連接(基準) | 8.563 | ~4.3 | 1x |
| 版本 1(逐字元檢查) | 19.844 | ~9.9 | ~2.3x |
| 版本 2(CAST + EXCEPTION) | 57.953 | ~29.0 | ~6.8x |
※ 版本 2 反而較慢,因例外處理機制開銷極大。
REGEXP_SUBSTR、SUBSTR、CASE)優先用 SQL,避免函數呼叫。DETERMINISTIC、RESULT_CACHE、PRAGMA UDF (12c+) 減少開銷。FORALL + 集合,或改寫為 Pipeline Table Function。DETERMINISTIC 函數並建立 Function-based Index 支援查詢。CREATE OR REPLACE FUNCTION f_extract_birthday_v1(...)
RETURN VARCHAR2 IS
c PLS_INTEGER; i PLS_INTEGER := 1; len PLS_INTEGER;
trans BOOLEAN := FALSE; ret VARCHAR2(4);
BEGIN
len := LENGTH(cert_no);
IF len = 15 THEN
WHILE i <= 15 LOOP
c := ASCII(SUBSTR(cert_no, i, 1));
IF c < 48 OR c > 57 THEN EXIT; END IF;
i := i + 1;
END LOOP;
IF i = 16 THEN trans := TRUE; ret := '19' || SUBSTR(cert_no, 7, 2); END IF;
ELSIF len = 18 THEN
WHILE i <= 17 LOOP ... END LOOP;
c := ASCII(SUBSTR(cert_no, 18, 1));
IF i = 18 AND (c = 88 OR c = 120 OR (c BETWEEN 48 AND 57)) THEN
trans := TRUE; ret := SUBSTR(cert_no, 7, 4);
END IF;
END IF;
-- 後續 birthday 兜底邏輯略
RETURN ret;
END;
CREATE OR REPLACE FUNCTION f_extract_birthday_v2(...)
RETURN VARCHAR2 IS
val NUMBER; ret VARCHAR2(4);
BEGIN
IF cert_type = '0' THEN
BEGIN
val := CAST(cert_no AS NUMBER); -- 15 碼
ret := '19' || SUBSTR(cert_no, 7, 2);
EXCEPTION WHEN VALUE_ERROR THEN NULL; END;
BEGIN
val := CAST(SUBSTR(cert_no,1,17) AS NUMBER); -- 18 碼
-- 檢查第 18 碼
ret := SUBSTR(cert_no, 7, 4);
EXCEPTION WHEN VALUE_ERROR THEN NULL; END;
END IF;
-- birthday 兜底同理用 CAST + EXCEPTION
RETURN ret;
END;