Oracle身份证校验函数
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 | 其他异常未知错误 |