Oracle身份证校验函数

一、身份证编码规则

1、十五位身份证编码规则

在这里插入图片描述
编码规则:地址码+出生日期码+顺序码+性别码
编码格式:dddddd yymmdd xx p
dddddd:6位地址码
yymmdd: 出生日期码,出生年(两位年)月日,(如:890302,对应具体日期19890302)
xx: 顺序码,系统产生,无法确定
p: 性别码,奇数为男,偶数为女

2、十八位身份证编码规则

在这里插入图片描述
编码规则:地址码+出生日期码+顺序码+校验码
编码格式:dddddd yyyymmdd xxx y
dddddd:6位地址码
yyyymmdd: 出生日期码,出生年(四位年)月日,如:19910215
xxx:顺序码,在同一地址码所标识的区域范围内,对同年、同月、同日出生的人编定的顺序号,顺序码的奇数分配给男性,偶数分配给女性。
y: 校验码,该位数值可通过前17位计算获得
校验位计算公式:Y_P = mod( ∑(Ai×Wi),11 )
i为身份证号码1…17 位; Y_P为校验码Y所在校验码数组位置
验证位 Y = [1,0,10,9,8,7,6,5,4,3,2]
前17位号码加权因子为Wi=[7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2]
如果验证码恰好是10,为了保证身份证是十八位,那么第十八位将用X来代替。

二、校验规则

1、验证长度

检验身份证号码长度是否为15位或18位,如果长度不等于15或18,则表示身份证号码位数错误。

2、验证是否含有非法字符

检验身份证号码中是否含有除了数字及X(x)之外的其他字符(15位身份证号码中只含有数字),如果包含其他字符,则表示身份证号码含有非法字符。

3、验证省(直辖市,自治区,特别行政区)编码

检验身份证号码的开头的第1-2位省(直辖市,自治区,特别行政区)编码是否在下面编码中:
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;(11-15 京津冀晋蒙,21-23辽吉黑,31-37沪苏浙皖闽赣鲁,41-46 豫鄂湘粤桂琼 ,50-54渝川贵云藏,61-65陕甘青宁新,81-82港澳等),如果不在上面编码中存在,则表示身份证号码地区编码错误。

4、验证年份

检验身份证号码中第7-10位(如果身份证号码长度是15位,则取第7-8位,并在前面加19组成出生年份)的年份,如果年份大于当前日期的年份或不是19、20开头,则表示身份证号码出生日期年份错误。

5、验证月份

检验身份证号码中第11-12位(如果身份证号码长度是15位,则取第9-10位)的月份,如果月份不在01-12内,则表示身份证号码出生日期月份错误。

6、验证日

检验身份证中第13-14位(如果身份证号码长度是15位,则取第11-12位)的日期,如果日期不在对应的年份的月份的日期中,则表示身份证号码出生日期的日期错误。

7、验证校验码

18位身份证号码对身份证号码前17位与对应加权因子相乘求和并除以11的取余数,根据余数在校验码组[1,0,X,9,8,7,6,5,4,3,2]中获取校验码,获取的校验码如果与第18位不相同,则表示身份证号码校验码错误。
Y_P = mod( ∑(Ai×Wi),11 )
Wi=[7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2]
Ai为身份证号码1…17 位

三、编码实现

1、正则表达式写法

CREATE OR REPLACE FUNCTION FN_IDCARDCHECK_REGEXP(IN_IDCARD IN VARCHAR2)
  RETURN NUMBER IS
  V_REGSTR    VARCHAR2(2000);
  V_SUM       NUMBER;
  V_MOD       NUMBER;
  V_CHECKBIT  CHAR(1);
  V_CHECKCODE CHAR(11) := '10X98765432';
  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
  /*返回值说明:
  0 身份证号码校验通过
  1 身份证号码位数错误
  2 身份证号码地区编码错误
  3 身份证号码含有非法字符或出生日期错误
  4 身份证号码校验码错误
  5 其他异常未知错误*/
  CASE LENGTHB(IN_IDCARD)
    WHEN 15 THEN
      IF INSTRB(V_AREACODE, SUBSTR(IN_IDCARD, 1, 2) || ',') = 0 THEN
        RETURN 2; --2 身份证号码地区编码错误
      END IF;
    
      IF MOD(TO_NUMBER('19' || SUBSTRB(IN_IDCARD, 7, 2)), 400) = 0 OR
         (MOD(TO_NUMBER('19' || SUBSTRB(IN_IDCARD, 7, 2)), 100) <> 0 AND
          MOD(TO_NUMBER('19' || SUBSTRB(IN_IDCARD, 7, 2)), 4) = 0) THEN
        V_REGSTR := '^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}$';
      ELSE
        V_REGSTR := '^[1-9][0-9]{5}[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}$';
      END IF;
    
      IF REGEXP_LIKE(IN_IDCARD, V_REGSTR) THEN
        RETURN 0; --0 身份证号码校验通过
      ELSE
        RETURN 3; --3 身份证号码含有非法字符或出生日期错误
      END IF;
    WHEN 18 THEN
      IF INSTRB(V_AREACODE, SUBSTRB(IN_IDCARD, 1, 2) || ',') = 0 THEN
        RETURN 2; --2 身份证号码地区编码错误
      END IF;
    
      IF MOD(TO_NUMBER(SUBSTRB(IN_IDCARD, 7, 4)), 400) = 0 OR
         (MOD(TO_NUMBER(SUBSTRB(IN_IDCARD, 7, 4)), 100) <> 0 AND
          MOD(TO_NUMBER(SUBSTRB(IN_IDCARD, 7, 4)), 4) = 0) THEN
        V_REGSTR := '^[1-9][0-9]{5}(19|20)[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|[1-2][0-9]))[0-9]{3}[0-9Xx]$';
      ELSE
        V_REGSTR := '^[1-9][0-9]{5}(19|20)[0-9]{2}((01|03|05|07|08|10|12)(0[1-9]|[1-2][0-9]|3[0-1])|(04|06|09|11)(0[1-9]|[1-2][0-9]|30)|02(0[1-9]|1[0-9]|2[0-8]))[0-9]{3}[0-9Xx]$';
      END IF;
    
      IF REGEXP_LIKE(IN_IDCARD, V_REGSTR) THEN
        V_SUM      := (TO_NUMBER(SUBSTRB(IN_IDCARD, 1, 1)) +
                      TO_NUMBER(SUBSTRB(IN_IDCARD, 11, 1))) * 7 +
                      (TO_NUMBER(SUBSTRB(IN_IDCARD, 2, 1)) +
                      TO_NUMBER(SUBSTRB(IN_IDCARD, 12, 1))) * 9 +
                      (TO_NUMBER(SUBSTRB(IN_IDCARD, 3, 1)) +
                      TO_NUMBER(SUBSTRB(IN_IDCARD, 13, 1))) * 10 +
                      (TO_NUMBER(SUBSTRB(IN_IDCARD, 4, 1)) +
                      TO_NUMBER(SUBSTRB(IN_IDCARD, 14, 1))) * 5 +
                      (TO_NUMBER(SUBSTRB(IN_IDCARD, 5, 1)) +
                      TO_NUMBER(SUBSTRB(IN_IDCARD, 15, 1))) * 8 +
                      (TO_NUMBER(SUBSTRB(IN_IDCARD, 6, 1)) +
                      TO_NUMBER(SUBSTRB(IN_IDCARD, 16, 1))) * 4 +
                      (TO_NUMBER(SUBSTRB(IN_IDCARD, 7, 1)) +
                      TO_NUMBER(SUBSTRB(IN_IDCARD, 17, 1))) * 2 +
                      TO_NUMBER(SUBSTRB(IN_IDCARD, 8, 1)) * 1 +
                      TO_NUMBER(SUBSTRB(IN_IDCARD, 9, 1)) * 6 +
                      TO_NUMBER(SUBSTRB(IN_IDCARD, 10, 1)) * 3;
        V_MOD      := MOD(V_SUM, 11);
        V_CHECKBIT := SUBSTRB(V_CHECKCODE, V_MOD + 1, 1);
      
        IF V_CHECKBIT = UPPER(SUBSTRB(IN_IDCARD, 18, 1)) THEN
          RETURN 0; --0 身份证号码校验通过
        ELSE
          RETURN 4; --4 身份证号码校验码错误
        END IF;
      ELSE
        RETURN 3; --3 身份证号码含有非法字符或出生日期错误
      END IF;
    ELSE
      RETURN 1; --1 身份证号码位数错误
  END CASE;
EXCEPTION
  WHEN OTHERS THEN
    RETURN 5; --5 其他异常未知错误
END FN_IDCARDCHECK_REGEXP;

检验结果代码检验结果描述
0身份证号码校验通过
1身份证号码位数错误
2身份证号码地区编码错误
3身份证号码含有非法字符或出生日期错误
4身份证号码校验码错误
5其他异常未知错误

2、非正则表达式写法

CREATE OR REPLACE FUNCTION FN_IDCARDCHECK(IN_IDCARD IN VARCHAR2)
  RETURN NUMBER IS
  V_SUM         NUMBER;
  V_MOD         NUMBER;
  V_LENGTH      NUMBER;
  V_DATE        VARCHAR2(10);
  V_ISNUMBER    BOOLEAN;
  V_ISNUMBER_17 BOOLEAN;
  V_CHECKBIT    CHAR(1);
  V_CHECKCODE   CHAR(11) := '10X98765432';
  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,';

  --验证身份证是否为数字
  FUNCTION FN_ISNUMBER(IN_STRING IN VARCHAR2) RETURN BOOLEAN IS
    I        NUMBER;
    V_NUM    NUMBER;
    V_FLAG   BOOLEAN;
    V_LENGTH NUMBER;
  BEGIN
    -- 通过ASCII码判断是否数字,介于[48, 57]之间,X或x为88或120。
    V_FLAG := TRUE;
    SELECT LENGTH(IN_STRING) INTO V_LENGTH FROM DUAL;
  
    FOR I IN 1 .. V_LENGTH LOOP
      V_NUM := ASCII(SUBSTR(IN_STRING, I, 1));
      IF V_NUM < 48 OR V_NUM > 57 THEN
        V_FLAG := FALSE;
        EXIT;
      END IF;
    END LOOP;
    RETURN V_FLAG;
  END FN_ISNUMBER;

  --验证身份证出身日期
  FUNCTION FN_ISDATE(IN_DATE IN VARCHAR2) RETURN BOOLEAN IS
    V_FLAG  BOOLEAN;
    V_YEAR  NUMBER;
    V_MONTH NUMBER;
    V_DAY   NUMBER;
  BEGIN
    --初始化
    V_FLAG := TRUE;
    --获取信息
    V_YEAR  := TO_NUMBER(SUBSTR(IN_DATE, 1, 4));
    V_MONTH := TO_NUMBER(SUBSTR(IN_DATE, 5, 2));
    V_DAY   := TO_NUMBER(SUBSTR(IN_DATE, 7, 2));
  
    --1, 3, 5, 7, 8, 10, 12月日期在1-31日
    IF V_MONTH IN (1, 3, 5, 7, 8, 10, 12) AND (V_DAY < 1 OR V_DAY > 31) THEN
      V_FLAG := FALSE;
    END IF;
    --4, 6, 9, 11月日期在1-30日
    IF V_MONTH IN (4, 6, 9, 11) AND (V_DAY < 1 OR V_DAY > 30) THEN
      V_FLAG := FALSE;
    END IF;
    --2月日期根据是否闰年判断是否在1-28或1-29日
    IF V_MONTH = 2 THEN
      --闰年
      IF (MOD(V_YEAR, 400) = 0) OR
         (MOD(V_YEAR, 100) <> 0 AND MOD(V_YEAR, 4) = 0) THEN
        IF (V_DAY < 1 OR V_DAY > 29) THEN
          V_FLAG := FALSE;
        END IF;
      ELSE
        --非闰年
        IF (V_DAY < 1 OR V_DAY > 28) THEN
          V_FLAG := FALSE;
        END IF;
      END IF;
    END IF;
    RETURN V_FLAG;
  END FN_ISDATE;

BEGIN
  /*返回值说明:
  0 身份证号码校验通过
  1 身份证号码位数错误
  2 身份证号码含有非法字符
  3 身份证号码地区编码错误
  4 身份证号码出生日期年份错误
  5 身份证号码出生日期月份错误
  6 身份证号码出生日期日错误
  7 身份证号码校验码错误
  8 其他异常未知错误*/
  --1 身份证号码位数错误
  SELECT LENGTHB(IN_IDCARD) INTO V_LENGTH FROM DUAL;
  IF V_LENGTH NOT IN (15, 18) THEN
    RETURN 1;
  END IF;

  --2 身份证号码含有非法字符
  IF V_LENGTH = 15 THEN
    V_ISNUMBER := FN_ISNUMBER(IN_IDCARD);
    IF NOT (V_ISNUMBER) THEN
      RETURN 2;
    END IF;
  ELSIF V_LENGTH = 18 THEN
    V_ISNUMBER    := FN_ISNUMBER(IN_IDCARD);
    V_ISNUMBER_17 := FN_ISNUMBER(SUBSTR(IN_IDCARD, 1, 17));
    IF NOT ((V_ISNUMBER) OR
        (V_ISNUMBER_17 AND UPPER(SUBSTR(IN_IDCARD, 18, 1)) = 'X')) THEN
      RETURN 2;
    END IF;
  END IF;

  --3 身份证号码地区编码错误
  IF INSTRB(V_AREACODE, SUBSTR(IN_IDCARD, 1, 2) || ',') = 0 THEN
    RETURN 3;
  END IF;

  --获取出生日期
  IF V_LENGTH = 15 THEN
    SELECT '19' || SUBSTR(IN_IDCARD, 7, 6) INTO V_DATE FROM DUAL;
  ELSIF V_LENGTH = 18 THEN
    SELECT SUBSTR(IN_IDCARD, 7, 8) INTO V_DATE FROM DUAL;
  END IF;

  --4 身份证号码出生日期年份错误
  IF SUBSTR(V_DATE, 1, 4) > TO_CHAR(SYSDATE, 'yyyy') THEN
    RETURN 4;
  END IF;

  --5 身份证号码出生日期月份错误
  IF TO_NUMBER(SUBSTR(V_DATE, 5, 2)) > 12 OR
     TO_NUMBER(SUBSTR(V_DATE, 5, 2)) < 1 THEN
    RETURN 5;
  END IF;

  --6 身份证号码出生日期日错误
  IF NOT FN_ISDATE(V_DATE) THEN
    RETURN 6;
  END IF;

  --  7 身份证号码校验码错误
  IF V_LENGTH = 18 THEN
    V_SUM      := (TO_NUMBER(SUBSTRB(IN_IDCARD, 1, 1)) +
                  TO_NUMBER(SUBSTRB(IN_IDCARD, 11, 1))) * 7 +
                  (TO_NUMBER(SUBSTRB(IN_IDCARD, 2, 1)) +
                  TO_NUMBER(SUBSTRB(IN_IDCARD, 12, 1))) * 9 +
                  (TO_NUMBER(SUBSTRB(IN_IDCARD, 3, 1)) +
                  TO_NUMBER(SUBSTRB(IN_IDCARD, 13, 1))) * 10 +
                  (TO_NUMBER(SUBSTRB(IN_IDCARD, 4, 1)) +
                  TO_NUMBER(SUBSTRB(IN_IDCARD, 14, 1))) * 5 +
                  (TO_NUMBER(SUBSTRB(IN_IDCARD, 5, 1)) +
                  TO_NUMBER(SUBSTRB(IN_IDCARD, 15, 1))) * 8 +
                  (TO_NUMBER(SUBSTRB(IN_IDCARD, 6, 1)) +
                  TO_NUMBER(SUBSTRB(IN_IDCARD, 16, 1))) * 4 +
                  (TO_NUMBER(SUBSTRB(IN_IDCARD, 7, 1)) +
                  TO_NUMBER(SUBSTRB(IN_IDCARD, 17, 1))) * 2 +
                  TO_NUMBER(SUBSTRB(IN_IDCARD, 8, 1)) * 1 +
                  TO_NUMBER(SUBSTRB(IN_IDCARD, 9, 1)) * 6 +
                  TO_NUMBER(SUBSTRB(IN_IDCARD, 10, 1)) * 3;
    V_MOD      := MOD(V_SUM, 11);
    V_CHECKBIT := SUBSTRB(V_CHECKCODE, V_MOD + 1, 1);
  
    IF V_CHECKBIT = UPPER(SUBSTRB(IN_IDCARD, 18, 1)) THEN
      RETURN 0;
    ELSE
      RETURN 7;
    END IF;
  ELSE
    RETURN 0;
  END IF;
  --8 其他异常未知错误
EXCEPTION
  WHEN OTHERS THEN
    RETURN 8;
END FN_IDCARDCHECK;
检验结果代码检验结果描述
0身份证号码校验通过
1身份证号码位数错误
2身份证号码含有非法字符
3身份证号码地区编码错误
4身份证号码出生日期年份错误
5身份证号码出生日期月份错误
6身份证号码出生日期日期错误
7身份证号码校验码错误
8其他异常未知错误
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐