有没有人可以使用PL / SQL在Oracle中使用工作函数,PL / SQL实现了Luhn Mod 16算法来生成输入代码编号的校验位,如下例所示? 0B012722900021AC35B2
LOGIC
从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
从字符串中的最后一个字符开始,向左移动,每隔一个数字加倍 -
成为0 22 0 2 2 14 2 4 9 0 0 0 2 2 10 24 3 10 11 4
转换" double"到Base 16(十六进制)格式。如果转换导致数字输出,请保留该值。
成为:0 16 0 2 2 E 2 4 9 0 0 0 2 2 10 18 3 A 11 4
通过将任何结果值分解为单个数字的长度来减少。
成为0 (1+6) 0 2 2 E 2 4 9 0 0 0 2 2 10 (1+8) 3 A 11 4
求和所有数字。应用从上一个计算序列返回的最后一个数值(如果当前值为A-F,则替换步骤1中的数值)
成为0 7 0 2 2 7 2 4 9 0 0 0 2 2 10 9 3 5 11 4
所有数字的总和为79 (0+7+0+2+2+7+2+4+9+0+0+0+2+2+10+9+3+5+11+4)
计算获得下一个16的倍数所需的值,在这种情况下,下一个16是80,因此值为1
相关的核对字符为1
谢谢李
答案 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?请编辑您的问题以确认相应的规则。