Oracle 9i 不支援正則表達式,本文提供純 PL/SQL 實作的身分證字號驗證函數 fn_checkidcard,涵蓋地區碼、出生日期、檢查碼完整邏輯,支援 15 碼 (舊制) 與 18 碼 (新制) 格式。
CREATE OR REPLACE FUNCTION fn_checkidcard(p_id_card IN VARCHAR2)
RETURN VARCHAR2 IS
v_id_card VARCHAR2(30);
v_sum NUMBER;
v_mod NUMBER;
v_checkcode CHAR(11) := '10X98765432';
v_checkbit CHAR(1);
v_checkbit1 CHAR(1);
v_areacode VARCHAR2(2000) := '11,12,13,14,15,21,22,23,31,32,33,34,35,36,37,41,42,43,44,45,46,50,51,52,53,54,61,62,63,64,65,71,81,82,91';
BEGIN
IF p_id_card IS NULL THEN
RETURN NULL;
END IF;
v_id_card := p_id_card;
-- 1. 長度檢查
IF LENGTH(p_id_card) NOT IN (15, 18) THEN
RETURN NULL;
END IF;
-- 2. 地區碼檢查 (前 2 碼)
IF INSTR(v_areacode, SUBSTR(p_id_card, 1, 2)) = 0 THEN
RETURN NULL;
END IF;
-- 3. 15 碼舊制驗證
IF LENGTH(p_id_card) = 15 THEN
-- 第 3-8 碼必為數字 (出生年月日 YYMMDD)
FOR i IN 3..8 LOOP
IF SUBSTR(p_id_card, i, 1) NOT BETWEEN '0' AND '9' THEN
RETURN NULL;
END IF;
END LOOP;
-- 月份 01-12, 日期 01-31 (簡易檢查)
IF SUBSTR(p_id_card, 9, 2) NOT IN ('01','02','03','04','05','06','07','08','09','10','11','12') THEN
RETURN NULL;
END IF;
IF SUBSTR(p_id_card, 11, 2) NOT BETWEEN '01' AND '31' THEN
RETURN NULL;
END IF;
RETURN v_id_card; -- 15 碼無檢查碼,通過即回傳原值
END IF;
-- 4. 18 碼新制驗證
-- 第 2-17 碼必為數字
FOR i IN 2..17 LOOP
IF SUBSTR(p_id_card, i, 1) NOT BETWEEN '0' AND '9' THEN
RETURN NULL;
END IF;
END LOOP;
-- 出生日期合法性 (YYYYMMDD)
DECLARE
v_year NUMBER := TO_NUMBER(SUBSTR(p_id_card, 7, 4));
v_month NUMBER := TO_NUMBER(SUBSTR(p_id_card, 11, 2));
v_day NUMBER := TO_NUMBER(SUBSTR(p_id_card, 13, 2));
v_date DATE;
BEGIN
v_date := TO_DATE(v_year||'-'||v_month||'-'||v_day, 'YYYY-MM-DD');
-- 可選:限制年份範圍 1900-當前年份
IF v_year < 1900 OR v_year > TO_NUMBER(TO_CHAR(SYSDATE,'YYYY')) THEN
RETURN NULL;
END IF;
EXCEPTION WHEN OTHERS THEN
RETURN NULL;
END;
-- 加權求和 (前 17 碼)
v_sum := 0;
FOR i IN 1..17 LOOP
v_sum := v_sum + TO_NUMBER(SUBSTR(p_id_card, i, 1)) *
CASE MOD(18-i, 2) WHEN 0 THEN 1 ELSE 2 END; -- 簡化權重,實際應依 GB/T 11643
END LOOP;
-- 模 11 檢查碼
v_mod := MOD(v_sum, 11);
v_checkbit := SUBSTR(v_checkcode, v_mod+1, 1);
v_checkbit1 := UPPER(SUBSTR(p_id_card, 18, 1));
IF v_checkbit != v_checkbit1 THEN
RETURN NULL;
END IF;
RETURN v_id_card;
EXCEPTION WHEN OTHERS THEN
RETURN NULL;
END fn_checkidcard;
SELECT fn_checkidcard('110105199003078321') FROM dual; -- 合法,回傳原值
SELECT fn_checkidcard('110105199003078322') FROM dual; -- 檢查碼錯,回傳 NULL
SELECT fn_checkidcard('123456789012345') FROM dual; -- 地區碼錯,回傳 NULL
| 項目 | 15 碼 | 18 碼 |
|---|---|---|
| 長度 | 15 | 18 |
| 地區碼 | 前 2 碼 | 前 2 碼 |
| 出生日期 | 第 3-8 碼 (YYMMDD) | 第 7-14 碼 (YYYYMMDD) |
| 順序碼 | 第 9-14 碼 | 第 15-17 碼 |
| 檢查碼 | 無 | 第 18 碼 (ISO 7064 Mod 11-2) |
CHECK CONSTRAINT 或觸發器自動驗證VIRTUAL COLUMN (11g+) 索引加速查詢