首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> Oracle 資料庫 >> Oracle 資料庫基礎

Oracle 9i 身分證字號驗證函數實作 (含 15/18 碼規則)

     編輯:Oracle 資料庫基礎

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 碼
長度1518
地區碼前 2 碼前 2 碼
出生日期第 3-8 碼 (YYMMDD)第 7-14 碼 (YYYYMMDD)
順序碼第 9-14 碼第 15-17 碼
檢查碼無第 18 碼 (ISO 7064 Mod 11-2)

部署建議

  • 建立 CHECK CONSTRAINT 或觸發器自動驗證
  • 搭配 VIRTUAL COLUMN (11g+) 索引加速查詢
  • 定期同步最新地區碼 (v_areacode)
  • 18 碼權重計算請依 GB/T 11643-2017 標準實作 (7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2)
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved