oracle 如何检查DB列中的值是否为BSN(荷兰语SSN)

xytpbqjk  于 2023-04-20  发布在  Oracle
关注(0)|答案(2)|浏览(205)

我正在寻找一种方法,您可以告诉您的列中的值是否是有效的BSN值。BSN(荷兰SSN)必须符合一种“11测试”。请注意,它与银行帐户不同。请查看https://nl.wikipedia.org/wiki/Burgerservicenummer了解详细信息。
我想返回的是一个0或1,表示我的BSN是有效的(=1)还是无效的(=0)。目前它存储为一个数字。
我尝试为此创建一个SQL语句。我从以下操作开始:
1.首先,我只需要有实际值的行,并跳过(null)值:

SELECT D_BSN FROM EMPLOYEE WHERE D_BSN IS NOT NULL;

1.在那之后,我希望我的BSN是VARCHAR2。

SELECT TO_CHAR(D_BSN) FROM EMPLOYEE WHERE D_BSN IS NOT NULL;

1.现在我希望我的BSN总是相同的长度:

SELECT DECODE(LENGTH(TO_CHAR(D_BSN)),8, '0' || D_BSN, D_BSN)
  FROM EMPLOYEE 
 WHERE D_BSN IS NOT NULL;

1.现在,如果我的长度不是9,我有一个无效的字段,所以'退出'与0。

SELECT 
    DECODE(
    LENGTH(DECODE(LENGTH(TO_CHAR(D_BSN)),8, '0' || D_BSN, D_BSN)), 9,
    DECODE(LENGTH(TO_CHAR(D_BSN)),8, '0' || D_BSN, D_BSN), 0)
   FROM MW_DOCUMENTS 
  WHERE D_BSN IS NOT NULL;

在这一点上,我发现我的SQL语句变得不可读。也是因为我还有很长的路要走。我必须选择每个数字,乘以它的位置,总结结果,除以11,看看mod是否等于BSN的最后一个数字。这在编程语言中很容易,所以为什么不把它放在一个函数中。所以我可以这样使用它:

SELECT * FROM EMPLOYEE WHERE IS_BSN(D_BSN) = 0

得到所有无效的BSN。看看我的答案。
UPDATE:(添加了函数的代码)
下面是我的函数,它返回一个0或1,表示有效或无效的BSN:

create or replace FUNCTION is_bsn(num_in NUMBER) RETURN NUMBER IS

-- Yes is BSN = True == 1 
-- NO not BSN = False == 0

 checkValue NUMBER := -1;
 factor NUMBER := 9;
 result NUMBER := 0;
 outcome NUMBER := 0;
 bsn VARCHAR2(9);
BEGIN
  bsn := to_char(num_in);
  -- Check if length is 8 if so add zero in front
  IF LENGTH(bsn) = 8 THEN
    bsn := '0' || bsn;
  END IF;
  -- Now length should be 9
  IF LENGTH(bsn) <> 9 THEN
    RETURN 0;
  END IF;
  checkValue := TO_NUMBER(SUBSTR(bsn, 9, 1));
  FOR Lcntr IN 1..8
  LOOP
    result := result + TO_NUMBER(SUBSTR(bsn, Lcntr, 1)) * factor;
    factor := factor - 1;
  END LOOP;
  outcome := result mod 11;
  if outcome = checkValue then
    return 1;
  ELSE
    return 0;
  END IF;
EXCEPTION
  WHEN OTHERS THEN
    RETURN 'FALSE';
END is_bsn;

更新:
由于Wildplasser的答案是针对Postgress的,而最初的问题是针对Oracle的,所以我将他的答案转换为Oracle:

create or replace FUNCTION is_bsn2(inBsn in NUMBER) RETURN NUMBER IS
-- Yes is BSN = True = 0 
-- NO not BSN = False  => 1
val NUMBER;
mul NUMBER;
bsn NUMBER := inBsn;
BEGIN
  val := 11 - (bsn mod 10); -- the check digit
  mul := 1;
  WHILE (bsn > 0) loop
     bsn := FLOOR (bsn / 10);
     mul := mul + 1;
     val := val + (bsn mod 10) * mul;
  END LOOP;
  IF (mul < 9) THEN
     RETURN 13; 
  END IF; -- too short
  RETURN val mod 11;
END;

请注意,我将其重命名为is_bsn2,并且结果〉0表示不是有效的bsn。我看不到一个比另一个有多大的性能提升,但我没有广泛地测试它。

zzzyeukh

zzzyeukh1#

所有这些都可以在一个循环中完成,而无需转换为字符。

  • 此版本接受整数参数。(9位十进制数应适合32位有符号整数)
  • 成功时返回0,否则返回〉0(这对我来说更有意义)
  • 不需要处理异常,因为不存在可能的异常。
  • 这是针对postgres的,但是差别应该是最小的。
CREATE function bsn_invalid(bsn INTEGER) RETURNS INTEGER
AS
$func$
DECLARE
        val INTEGER;
        mul INTEGER;
BEGIN
        val := 11- (bsn % 10); -- the check digit
        mul := 1;
        while (bsn > 0) loop
                bsn := (bsn / 10);
                mul := mul + 1;
                val := val + (bsn%10)*mul;
        end loop;
        if (mul < 9) then return 13; END IF; -- too short
        return val % 11;
END
$func$ LANGUAGE plpgsql
        ;

 -- test it::
SELECT bsn_invalid( 123456782);
SELECT bsn_invalid( 111222333 );
SELECT bsn_invalid( 23456784); -- assume a leading zero
SELECT bsn_invalid( 1234567);  -- too short
3vpjnl9f

3vpjnl9f2#

对于不喜欢函数的人;下面是一个嵌入在select语句中解决方案:

SELECT *
FROM patient
WHERE bsn LIKE '%[0-9]%' AND (
    (9 * CAST(SUBSTRING(bsn, 1, 1) AS INTEGER)) +
    (8 * CAST(SUBSTRING(bsn, 2, 1) AS INTEGER)) +
    (7 * CAST(SUBSTRING(bsn, 3, 1) AS INTEGER)) +
    (6 * CAST(SUBSTRING(bsn, 4, 1) AS INTEGER)) +
    (5 * CAST(SUBSTRING(bsn, 5, 1) AS INTEGER)) +
    (4 * CAST(SUBSTRING(bsn, 6, 1) AS INTEGER)) +
    (3 * CAST(SUBSTRING(bsn, 7, 1) AS INTEGER)) +
    (2 * CAST(SUBSTRING(bsn, 8, 1) AS INTEGER)) -
    (1 * CAST(SUBSTRING(bsn, 9, 1) AS INTEGER))
) % 11 = 0

WHERE bsn LIKE '%[0-9]%'检查字段的格式(空值,值等),然后执行实际的11 proef。
这适用于几乎任何rdbm类型。也许只是切换CAST()与CONVERT()或其他取决于你的数据库风格。

相关问题