2019-0910

oracle 身份证校验函数

作者: momy 分类: 数据库 0 Comment »
摘要:oracle 身份证校验函数

1、正则表达式写法:

CREATE OR REPLACE FUNCTION fn_checkidcard (p_idcard IN VARCHAR2) RETURN INT
IS
   v_regstr      VARCHAR2 (2000);
   v_sum         NUMBER;
   v_mod         NUMBER;
   v_checkcode   CHAR (11)       := '10X98765432';
   v_checkbit    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
   CASE LENGTHB (p_idcard)
      WHEN 15
      THEN                                                            -- 15位
         IF INSTRB (v_areacode, SUBSTR (p_idcard, 1, 2) || ',') = 0 THEN
            RETURN 0;
         END IF;
         IF MOD (TO_NUMBER (SUBSTRB (p_idcard, 7, 2)) + 1900, 400) = 0
            OR
            (
                MOD (TO_NUMBER (SUBSTRB (p_idcard, 7, 2)) + 1900, 100) <> 0
                AND
                MOD (TO_NUMBER (SUBSTRB (p_idcard, 7, 2)) + 1900, 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 (p_idcard, v_regstr) THEN
            RETURN 1;
         ELSE
            RETURN 0;
         END IF;
      WHEN 18
      THEN                                                             -- 18位
         IF INSTRB (v_areacode, SUBSTRB (p_idcard, 1, 2) || ',') = 0 THEN
            RETURN 0;
         END IF;
                       
         IF MOD (TO_NUMBER (SUBSTRB (p_idcard, 7, 4)), 400) = 0
            OR
            (
                MOD (TO_NUMBER (SUBSTRB (p_idcard, 7, 4)), 100) <> 0
                AND
                MOD (TO_NUMBER (SUBSTRB (p_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 (p_idcard, v_regstr) THEN
            v_sum :=
                   (  TO_NUMBER (SUBSTRB (p_idcard, 1, 1))
                    + TO_NUMBER (SUBSTRB (p_idcard, 11, 1))
                   )
                 * 7
               +   (  TO_NUMBER (SUBSTRB (p_idcard, 2, 1))
                    + TO_NUMBER (SUBSTRB (p_idcard, 12, 1))
                   )
                 * 9
               +   (  TO_NUMBER (SUBSTRB (p_idcard, 3, 1))
                    + TO_NUMBER (SUBSTRB (p_idcard, 13, 1))
                   )
                 * 10
               +   (  TO_NUMBER (SUBSTRB (p_idcard, 4, 1))
                    + TO_NUMBER (SUBSTRB (p_idcard, 14, 1))
                   )
                 * 5
               +   (  TO_NUMBER (SUBSTRB (p_idcard, 5, 1))
                    + TO_NUMBER (SUBSTRB (p_idcard, 15, 1))
                   )
                 * 8
               +   (  TO_NUMBER (SUBSTRB (p_idcard, 6, 1))
                    + TO_NUMBER (SUBSTRB (p_idcard, 16, 1))
                   )
                 * 4
               +   (  TO_NUMBER (SUBSTRB (p_idcard, 7, 1))
                    + TO_NUMBER (SUBSTRB (p_idcard, 17, 1))
                   )
                 * 2
               + TO_NUMBER (SUBSTRB (p_idcard, 8, 1)) * 1
               + TO_NUMBER (SUBSTRB (p_idcard, 9, 1)) * 6
               + TO_NUMBER (SUBSTRB (p_idcard, 10, 1)) * 3;
            v_mod := MOD (v_sum, 11);
            v_checkbit := SUBSTRB (v_checkcode, v_mod + 1, 1);
            IF v_checkbit = upper(substrb(p_idcard,18,1)) THEN
               RETURN 1;
            ELSE
               RETURN 0;
            END IF;
         ELSE
            RETURN 0;
         END IF;
      ELSE
         RETURN 0;                                      -- 身份证号码位数不对
   END CASE;
EXCEPTION
   WHEN OTHERS
   THEN
      RETURN 0;
END fn_checkidcard;


2、非正则表达式写法

Create Or Replace Function Func_checkIdcard (p_idcard in varchar2) Return Number
Is
    v_sum         Number;
    v_mod         Number;
    v_length      Number;
    v_date        Varchar2(10);
    v_isDate      Boolean;
    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,';
        
    --[isNumber]--
    Function isNumber (p_string in varchar2) Return Boolean
    Is
        i           number;
        k           number;
        flag        boolean;
        v_length    number;
    Begin
        /*
        算法:
            通过ASCII码判断是否数字,介于[48, 57]之间。
            select ascii('0'),ascii('1'),ascii('2'),ascii('3'),ascii('4'),ascii('5'),ascii('6'),ascii('7'),ascii('8'),ascii('9') from dual;
        */
            
        flag := True;
        select length(p_string) into v_length from dual;
            
        for i in 1..v_length loop
            k := ascii(substr(p_string,i,1));
            if k < 48 or k > 57 then
                flag := False;
                Exit;
            end if;
        end loop;
            
        Return flag;
    End isNumber;
        
    --[isDate]--
    Function isDate (p_date in varchar2) Return Boolean
    Is
        v_flag          boolean;
        v_year          number;
        v_month         number;
        v_day           number;
        v_isLeapYear    boolean;
    Begin
        --[初始化]--
        v_flag := True;
            
        --[获取信息]--
        v_year  := to_number(substr(p_date,1,4));
        v_month := to_number(substr(p_date,5,2));
        v_day   := to_number(substr(p_date,7,2));
            
        --[判断是否为闰年]--
        if (mod(v_year,400) = 0) Or (mod(v_year,100) <> 0 And mod(v_year,4) = 0) then
            v_isLeapYear := True;
        else
            v_isLeapYear := False;
        end if;
            
        --[判断月份]--
        if v_month < 1 Or v_month > 12 then
            v_flag := False;
            Return v_flag;
        end if;
            
        --[判断日期]--
        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;
        if v_month in (4,6,9,11) and (v_day < 1 or v_day > 30) then
            v_flag := False;
        end if;
        if v_month in (2) then
            if (v_isLeapYear) 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 isDate;
Begin
    /*
    返回值说明:
        -1      身份证号码位数不对
        -2      身份证号码出生日期超出范围
        -3      身份证号码含有非法字符
        -4      身份证号码校验码错误
        -5      身份证号码地区码非法
        1       身份证号码通过校验
    */
    --[长度校验]--
    select lengthb(p_idcard) into v_length from dual;
    if v_length not in (15,18) then
        return -1;
    end if;
        
    --[区位码校验]--
    if instrb(v_areacode, substr(p_idcard, 1, 2)||',') = 0 then
        return -5;
    end if;
        
    --[格式化校验]--
    if v_length = 15 then
        v_isNumber := isNumber (p_idcard);
        if not (v_isNumber) then
            return -3;
        end if;
    elsif v_length = 18 then
        v_isNumber    := isNumber (p_idcard);
        v_isNumber_17 := isNumber (substr(p_idcard,1,17));
        if not ((v_isNumber) or (v_isNumber_17 and upper(substr(p_idcard,18,1)) = 'X')) then
            return -3;
        end if;
    end if;
        
    --[出生日期校验]--
    if v_length = 15 then
        select '19'||substr(p_idcard,7,6) into v_date from dual;
    elsif v_length = 18 then
        select substr(p_idcard,7,8) into v_date from dual;
    end if;
    v_isDate := isDate (v_date);
    if not (v_isDate) then
        return -2;
    end if;
        
    --[校验码校验]--
    if v_length = 18 then
        v_sum :=
               (  TO_NUMBER (SUBSTRB (p_idcard, 1, 1))
                + TO_NUMBER (SUBSTRB (p_idcard, 11, 1))
               )
             * 7
           +   (  TO_NUMBER (SUBSTRB (p_idcard, 2, 1))
                + TO_NUMBER (SUBSTRB (p_idcard, 12, 1))
               )
             * 9
           +   (  TO_NUMBER (SUBSTRB (p_idcard, 3, 1))
                + TO_NUMBER (SUBSTRB (p_idcard, 13, 1))
               )
             * 10
           +   (  TO_NUMBER (SUBSTRB (p_idcard, 4, 1))
                + TO_NUMBER (SUBSTRB (p_idcard, 14, 1))
               )
             * 5
           +   (  TO_NUMBER (SUBSTRB (p_idcard, 5, 1))
                + TO_NUMBER (SUBSTRB (p_idcard, 15, 1))
               )
             * 8
           +   (  TO_NUMBER (SUBSTRB (p_idcard, 6, 1))
                + TO_NUMBER (SUBSTRB (p_idcard, 16, 1))
               )
             * 4
           +   (  TO_NUMBER (SUBSTRB (p_idcard, 7, 1))
                + TO_NUMBER (SUBSTRB (p_idcard, 17, 1))
               )
             * 2
           + TO_NUMBER (SUBSTRB (p_idcard, 8, 1)) * 1
           + TO_NUMBER (SUBSTRB (p_idcard, 9, 1)) * 6
           + TO_NUMBER (SUBSTRB (p_idcard, 10, 1)) * 3;
        v_mod := MOD (v_sum, 11);
        v_checkbit := SUBSTRB (v_checkcode, v_mod + 1, 1);
            
        if v_checkbit = upper(substrb(p_idcard,18,1)) then
           return 1;
        else
           return -4;
        end if;
    else
        return 1;
    end if;
End Func_checkIdcard;


标签: Oracle 身份证校验 阅读: 247
上一篇: 全面解析“网络中部分熊猫烧香病毒代码” - 3179次

向右滑动解锁留言