需要Luhn Modulus 16编码示例在PL / SQL中生成校验位

时间:2018-01-04 10:07:36

标签: oracle stored-procedures plsql luhn

有没有人可以使用PL / SQL在Oracle中使用工作函数,PL / SQL实现了Luhn Mod 16算法来生成输入代码编号的校验位,如下例所示? 0B012722900021AC35B2

LOGIC

  1. 从HEX映射到十进制等效0 B 0 1 2 7 2 2 9 0 0 0 2 1 A C 3 5 B 2 - 变为0 11 0 1 2 7 2 2 9 0 0 0 2 1 10 12 3 5 11 2

  2. 从字符串中的最后一个字符开始,向左移动,每隔一个数字加倍 - 成为0 22 0 2 2 14 2 4 9 0 0 0 2 2 10 24 3 10 11 4

  3. 转换" double"到Base 16(十六进制)格式。如果转换导致数字输出,请保留该值。 成为:0 16 0 2 2 E 2 4 9 0 0 0 2 2 10 18 3 A 11 4

  4. 通过将任何结果值分解为单个数字的长度来减少。 成为0 (1+6) 0 2 2 E 2 4 9 0 0 0 2 2 10 (1+8) 3 A 11 4

  5. 求和所有数字。应用从上一个计算序列返回的最后一个数值(如果当前值为A-F,则替换步骤1中的数值) 成为0 7 0 2 2 7 2 4 9 0 0 0 2 2 10 9 3 5 11 4

  6. 所有数字的总和为79 (0+7+0+2+2+7+2+4+9+0+0+0+2+2+10+9+3+5+11+4)

  7. 计算获得下一个16的倍数所需的值,在这种情况下,下一个16是80,因此值为1

  8. 相关的核对字符为1

  9. 谢谢李

2 个答案:

答案 0 :(得分:1)

这个怎么样?

CREATE OR REPLACE TYPE VARCHAR_TABLE_TYPE AS TABLE OF VARCHAR2(1000);


DECLARE
    luhn VARCHAR2(100) := '0B012722900021AC35B2';

    digits VARCHAR_TABLE_TYPE;
    DigitSum INTEGER;
BEGIN

    SELECT REGEXP_SUBSTR(luhn, '.', 1, LEVEL)
    BULK COLLECT INTO digits
    FROM dual
    CONNECT BY REGEXP_SUBSTR(luhn, '.', 1, LEVEL) IS NOT NULL;  

    FOR i IN digits.FIRST..digits.LAST LOOP
        digits(i) := TO_NUMBER(digits(i), 'X'); -- Map from HEX into Decimal equivalent
        IF digits.COUNT MOD 2 = i MOD 2 THEN -- every second digit from left            
            digits(i) := 2 * TO_NUMBER(digits(i)); -- doubling number
            digits(i) := TO_CHAR(digits(i), 'fmXX'); -- Convert the "double" to a Base 16 (Hexadecimal) format
            IF (REGEXP_LIKE(digits(i), '^\d+$')) THEN
                -- Reduce by splitting down any resultant values over a single digit in length. 
                SELECT SUM(REGEXP_SUBSTR(digits(i), '\d', 1, LEVEL))
                INTO digits(i)
                FROM dual
                CONNECT BY REGEXP_SUBSTR(digits(i), '\d', 1, LEVEL) IS NOT NULL;
            END IF;    
        END IF;
    END LOOP;

    FOR i IN digits.FIRST..digits.LAST LOOP
        -- I don't understand step 5), let's simulate it
        IF digits(i) = 'E' THEN digits(i) := 7; END IF;
        IF digits(i) = 'A' THEN digits(i) := 5; END IF;
    END LOOP;

    -- The sum of all digits
    SELECT SUM(COLUMN_VALUE)
    INTO DigitSum
    FROM TABLE(digits);

    -- Calculate the value needed to obtain the next multiple of 16 
    DBMS_OUTPUT.PUT_LINE ( 16 - DigitSum MOD 16 );

END;

答案 1 :(得分:0)

这是一个实现您在问题中概述的步骤的函数:

create or replace function get_luhn_16_check_digit 
    ( p_str in varchar)
    return pls_integer 
is
    b16 sys.dbms_debug_vc2coll := sys.dbms_debug_vc2coll() ;
    n16 sys.odcinumberlist := sys.odcinumberlist();
    tot simple_integer := 0;
    chk_digit pls_integer;
begin
    -- step 1)
    select to_number(tkn, 'X')
    bulk collect into n16
    from ( select substr(p_str, level, 1) as tkn
                 from dual
                 connect by level <= length(p_str));
    b16.extend(n16.count());

    for idx in 1..n16.count() loop
        if mod(idx, 2) = 0
        then 
            -- step 2) and 3)
            b16(idx) := to_char( to_char(n16(idx)) * 2, 'fmXX');
            -- step 4)
            if length( b16(idx)) = 2 then
                 b16(idx) := to_number(substr(b16(idx),1,1)) + to_number(substr(b16(idx),2,1));
            end if;  
        else 
            b16(idx) := trim(to_char(n16(idx)));
        end if;
    end loop;

    -- step 5) and 6) 
    for idx in 1..b16.count() loop
        if  b16(idx) not in ('A','B','C','D','E','F') then
            tot := tot + to_number(b16(idx));
        else
            tot := tot + n16(idx);
        end if;
    end loop;

    -- step 7) and 8)
    chk_digit := (ceil(tot/16)*16) - tot; 
    return chk_digit;
end;
/

运行:

select get_luhn_16_check_digit('0B012722900021AC35B2') from dual;

现在返回1。这是a LiveSQL demo

当模数16大于9时,仍然存在问题;例如,此值返回15

select get_luhn_16_check_digit('22111111111111111111') from dual; 

小数结果(例如15)显然不是检查数字,这表示您需要额外的步骤9)。也许我们需要转换15 - &gt; (1+5) - &gt; 6。或者数字应该是base16?请编辑您的问题以确认相应的规则。