MySQL自定义函数校验身份证号有效性

1.创建校验码函数

DROP FUNCTION IF EXISTS VALIDATE_CHECK_CODE;
CREATE FUNCTION VALIDATE_CHECK_CODE(ID_CARD VARCHAR(18))
/*
身份证号校验规则:
1.将前面的身份证号码17位数分别乘以不同的系数。第i位对应的数为[2^(18-i)]mod11。从第一位到第十七位的系数分别为:7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 ;
2.将这17位数字和系数相乘的结果相加;
3.用加出来和除以11,看余数是多少?;
4.余数只可能有0 1 2 3 4 5 6 7 8 9 10这11个数字。其分别对应的最后一位身份证的号码为1 0 X 9 8 7 6 5 4 3 2;
*/
RETURNS VARCHAR(50) CHARSET utf8
DETERMINISTIC
BEGIN
    DECLARE WEIGHT_FACTOR VARCHAR(40) DEFAULT '7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2';   -- 身份证系数
    DECLARE CHECK_CODE VARCHAR(10) DEFAULT '';
	DECLARE V_MOD VARCHAR(25) DEFAULT '1,0,X,9,8,7,6,5,4,3,2';    -- 求MOD得的余数
	DECLARE V_CODE_TEMP VARCHAR(10) DEFAULT '';
	DECLARE V_MODCODE VARCHAR(25) DEFAULT '0,1,2,3,4,5,6,7,8,9,X';    -- 校验位
	DECLARE SUM_WEIGHT INT DEFAULT 0;
	DECLARE I INT DEFAULT 1;

    WHILE I <= 17 DO
	   SET SUM_WEIGHT = SUM_WEIGHT + SUBSTRING(ID_CARD, I, 1) * SUBSTRING_INDEX(SUBSTRING_INDEX(WEIGHT_FACTOR, ',', I), ',', -1);
       SET I := I + 1;
    END WHILE;

	SET V_CODE_TEMP := MOD(SUM_WEIGHT, 11);
    IF V_CODE_TEMP = '10' THEN
        SET V_CODE_TEMP := 'X';
    END IF;
	
	SELECT CASE V_CODE_TEMP
	   WHEN '1' THEN '0' 
	   WHEN '0' THEN '1'
	   WHEN 'X' THEN '2' 
	   WHEN '9' THEN '3' 
	   WHEN '8' THEN '4' 
	   WHEN '7' THEN '5' 
	   WHEN '6' THEN '6' 
	   WHEN '5' THEN '7' 
	   WHEN '4' THEN '8' 
	   WHEN '3' THEN '9' 
	   WHEN '2' THEN 'X' 
	END INTO V_MODCODE;
	
	SET  CHECK_CODE := V_MODCODE;

   RETURN CHECK_CODE;
END;

2.创建身份证号校验函数

DROP FUNCTION IF EXISTS FUN_VERIFY_CERT;
CREATE FUNCTION FUN_VERIFY_CERT(ID_NUMBER VARCHAR(20))
    RETURNS BOOLEAN
    DETERMINISTIC
	/* @Param ID_NUMBER 身份证号 */
    BEGIN
       DECLARE V_TEMP VARCHAR(50) DEFAULT '';-- 临时变量
       DECLARE V_HANDLER VARCHAR(50) DEFAULT 'FAIL';  -- 临时变量
       DECLARE V_TMP_15_18 VARCHAR(50);  -- 18 15位证件号
       DECLARE V_ID_SUM  BIGINT DEFAULT 0;  -- 证件系数求和
       DECLARE V_ID_SEX VARCHAR(10) DEFAULT '';  -- 证件性别
       DECLARE V_ID_BIRTH VARCHAR(10) DEFAULT '';  -- 证件出生日期
       DECLARE V_IM_TEMP VARCHAR(50); -- MOD临时
	
	   -- 判断非空
       IF (ID_NUMBER IS NULL OR ID_NUMBER = '') THEN
           RETURN FALSE;
	   ELSE
	       SET ID_NUMBER := UPPER(ID_NUMBER);   -- 将身份证号转为大写模式
       END IF;

	   -- 判断长度
       IF (LENGTH(ID_NUMBER) <> 15 AND LENGTH(ID_NUMBER) <> 18) THEN
           RETURN FALSE;
       END IF;
	   
	   -- 判断输入合法性
	   /*
           这个语句使用了正则表达式 ^[1-9]\d+[0-9Xx]$ 来匹配符合规范的身份证号码。
           其中:
           ^ 表示开头;
           [1-9] 表示第一位为非零数字;
           \d{16} 表示后面16位为数字;
           [0-9Xx] 表示最后一位为数字或字母X或x;
           $ 表示结尾。
       */
	   IF ID_NUMBER NOT REGEXP '^[1-9]\\d+[0-9Xx]$' THEN
	       RETURN FALSE;
       END IF;

       -- 15位证件号校验
       IF (LENGTH(ID_NUMBER) = 15) THEN
           BEGIN
               -- 15位身份证号不存在校验位
               -- 性别取值:0-女 1-男
               SELECT CASE MOD((CAST(SUBSTR(ID_NUMBER, 15, 1) AS SIGNED)), 2)
                      WHEN 0 THEN '0' ELSE '1' END INTO V_ID_SEX;
               -- 比较性别
               IF (V_ID_SEX NOT IN ('0', '1')) THEN
                   RETURN FALSE;
               END IF;

               -- 出生时间取值
               SET @year  := CONCAT('19',SUBSTR(V_TMP_15_18, 7, 2));-- 年
               SET @month := SUBSTR(V_TMP_15_18, 9, 2);-- 月
               SET @day   := SUBSTR(V_TMP_15_18, 11, 2);-- 日
               SET V_ID_BIRTH := CONCAT(@year,'-',@month,'-',@day);
			   SELECT V_ID_BIRTH REGEXP '(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|1[0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[13579][26])00))-02-29)$' INTO V_TEMP;
               IF (V_TEMP = '0') THEN
                   RETURN FALSE;
               END IF;
	   		-- 设置返回值
	   		SET V_HANDLER := 'SUCCESS';
           END;
       END IF;

       -- 18位证件号校验
       IF (LENGTH(ID_NUMBER) = 18) THEN
           BEGIN
               SET V_TEMP := '';
               SET V_TMP_15_18 := ID_NUMBER;

               -- 将得到的总合除以11得到一个余数,余数对应相应值
               SET V_IM_TEMP := VALIDATE_CHECK_CODE(V_TMP_15_18);
               SET V_TEMP := UPPER(SUBSTR(V_TMP_15_18, 18, 1));

               -- -- 校验位比较 第18位为校验位
               IF V_IM_TEMP <> V_TEMP THEN
                   RETURN FALSE;
               END IF;

               -- 性别取值:0-女 1-男
               SELECT CASE MOD((CAST(SUBSTR(ID_NUMBER, 17, 1) AS SIGNED)), 2)
                      WHEN 0 THEN '0' ELSE '1' END INTO V_ID_SEX;
               -- 比较性别
               IF (V_ID_SEX NOT IN ('0', '1')) THEN
                   RETURN FALSE;
               END IF;

               -- 出生时间取值
                SET @year  := SUBSTR(V_TMP_15_18, 7, 4);-- 年
                SET @month := SUBSTR(V_TMP_15_18, 11,2);-- 月
                SET @day   := SUBSTR(V_TMP_15_18, 13,2);-- 日
                SET V_ID_BIRTH := CONCAT(@year,'-',@month,'-',@day);
				SELECT V_ID_BIRTH REGEXP '(([0-9]{3}[1-9]|[0-9]{2}[1-9][0-9]{1}|[0-9]{1}[1-9][0-9]{2}|[1-9][0-9]{3})-(((0[13578]|1[02])-(0[1-9]|[12][0-9]|3[01]))|((0[469]|11)-(0[1-9]|[12][0-9]|30))|(02-(0[1-9]|1[0-9]|2[0-8]))))|((([0-9]{2})(0[48]|[2468][048]|[13579][26])|((0[48]|[2468][048]|[13579][26])00))-02-29)$' INTO V_TEMP;
                IF (V_TEMP = '0') THEN
                    RETURN FALSE;
                END IF;
	   		-- 设置返回值
	   		SET V_HANDLER := 'SUCCESS';
           END;
       END IF;
       -- 最后一步无问题,返回FALSE
       IF V_HANDLER <> 'SUCCESS' THEN
           RETURN FALSE;
       END IF;
       RETURN TRUE;
    END;

3.校验测试

mysql> SELECT FUN_VERIFY_CERT('142223198310300212') AS CheckResul;
+------------+
| CheckResul |
+------------+
|          0 |
+------------+
1 row in set (0.00 sec)

mysql> SELECT FUN_VERIFY_CERT('230223197902222711') AS CheckResul;
+------------+
| CheckResul |
+------------+
|          1 |
+------------+
1 row in set (0.00 sec)

你可能感兴趣的:(mysql运维管理,mysql,数据库)