T-SQL :: 生成随机的瑞士国民身份证号码 (AHV/AVS)

T-SQL :: generate random Swiss National Identification Number (AHV/AVS)

我想生成随机的瑞士国民身份证号码 (AHV/AVS)。

我找到了一个 can do that and if I look at the source code of the same page I can alos see the JavaScript code that can generate it.

的网站

数字是按照以下模式生成的:

  1. 756: 是前缀号,永远不变
  2. 1234: 是一个随机数
  3. 5678: 是一个随机数
  4. 9: 是一个随机数
  5. 7:就是这个计算产生的控制号
    • 从第一个数字开始,每隔一个数字求和:7 + 6 + 2 + 4 + 6 + 8 = 33
    • 从 drcond 数开始,每隔一个数取其和:5 + 1 + 3 + 5 + 7 + 9 = 30
    • 然后将第二个数 x 3 相乘并对第一个数求和: 33 + (30 x 3) = 123
    • 现在让 10 减去那个数的模:10-(123%10) = 10-3 = 7

===> And this is how we finally have obtained 7 which is the last number <===

我创建了一个SQL命令可以生成我需要的随机数:

SELECT CONCAT('756.', 
FLOOR(RAND(CHECKSUM(NEWID()))*(9999-1000+1)+1000) , '.',
FLOOR(RAND(CHECKSUM(NEWID()))*(9999-1000+1)+1000) , '.',
ABS(CHECKSUM(NEWID()))%10
-- How to select one number out of two? 
)

此代码生成我需要的所有随机数,但我不知道如何 select 二选一。

不知道T-SQL能不能解析数字和select二选一

首先,您当前的逻辑存在缺陷,您得到了一个介于 0 和 9999 之间的随机数,但是您没有将该值设置为固定宽度。因此,如果您的随机数是 792,您最终会得到 792 而不是 00700090002。您需要添加前导零。我使用 CONCATRIGHT.

接下来,我将表达式移到 FROM 中,使其具体化并易于使用。然后我们可以使用 SUBSTRINGCONVERT 来获取你的数字的 2 部分和 SUM 它们,然后最后应用最终逻辑。然后我将句点注入字符串。

SELECT V.NIN,
       STUFF(STUFF(STUFF(CONCAT(V.NIN,10 - ((Odds + (Evens * 3)) % 10)),12,0,'.'),8,0,'.'),4,0,'.')
FROM (VALUES(CONCAT(RIGHT(CONCAT('000','756'),3), 
                    RIGHT(CONCAT('0000',FLOOR(RAND(CHECKSUM(NEWID()))*(9999-1000+1)+1000)),4),
                    RIGHT(CONCAT('0000',FLOOR(RAND(CHECKSUM(NEWID()))*(9999-1000+1)+1000)),4),
                    ABS(CHECKSUM(NEWID()))%10)),
            ('756123456789'))V(NIN)
     CROSS APPLY (VALUES(CONVERT(int,SUBSTRING(V.NIN,1,1)) + CONVERT(int,SUBSTRING(V.NIN,3,1)) + CONVERT(int,SUBSTRING(V.NIN,5,1)) + CONVERT(int,SUBSTRING(V.NIN,7,1)) + CONVERT(int,SUBSTRING(V.NIN,9,1)) + CONVERT(int,SUBSTRING(V.NIN,11,1)),
                         CONVERT(int,SUBSTRING(V.NIN,2,1)) + CONVERT(int,SUBSTRING(V.NIN,4,1)) + CONVERT(int,SUBSTRING(V.NIN,6,1)) + CONVERT(int,SUBSTRING(V.NIN,8,1)) + CONVERT(int,SUBSTRING(V.NIN,10,1)) + CONVERT(int,SUBSTRING(V.NIN,12,1))))I(Odds,Evens)

但是,老实说,我建议这可能适合您的应用程序,而不是 SQL。

此实现使用整数计算而不是子字符串来获取数字。

注意:我在校验位计算的最终结果中加上了%10,转换为10 -> 0。

-- Format Number
SELECT *, CAST( Prefix AS CHAR( 3 )) + '.' + CAST( Rnd1 AS CHAR( 4 )) + '.' + CAST( Rnd2 AS CHAR( 4 )) + '.' + CAST( Rnd3 AS CHAR( 1 )) + CAST( CheckDigit AS CHAR( 1 )) AS FinalNum
FROM(
    -- Step 2: calculate check digit. NOTE: I apply %10 to the result to convert 10 to 0
    SELECT *, ( 10 - (( OddDigits + ( EvenDigits ) * 3 ) % 10 )) % 10 AS CheckDigit
    FROM 
        -- Step 2: calculate Odd / Even digit sum. Note that prefix is hardcoded to 756
        ( SELECT Prefix, Rnd1, Rnd2, Rnd3,
            7 + 6 + (( Rnd1 % 1000 ) / 100 ) + ( Rnd1 % 10 ) + (( Rnd2 % 1000 ) / 100 ) + ( Rnd2 % 10 ) AS OddDigits,
            5 + ( Rnd1 / 1000 ) + (( Rnd1 % 100 ) / 10 ) + ( Rnd2 / 1000 ) + (( Rnd2 % 100 ) / 10 ) + Rnd3 AS EvenDigits
        FROM
            -- Step 1: initial random values. Note that prefix is hardcoded to 756
            ( VALUES( 756,
                CAST( FLOOR( RAND( CHECKSUM( NEWID())) * ( 9999 - 1000 ) + 1000 ) AS INT ),
                CAST( FLOOR( RAND( CHECKSUM( NEWID())) * ( 9999 - 1000 ) + 1000 ) AS INT ),
                ABS( CHECKSUM( NEWID())) % 10 ),
                -- Your original example. Should be removed in production code
                (756, 1234, 5678, 9))   AS RndNum( Prefix, Rnd1, Rnd2, Rnd3 )
        ) AS CtrlDigitCalculation
    ) AS FormattedNumber

结果

Prefix      Rnd1        Rnd2        Rnd3        OddDigits   EvenDigits  CheckDigit  FinalNum
----------- ----------- ----------- ----------- ----------- ----------- ----------- ----------------
756         3826        4185        8           34          34          9           756.8002.7335.52
756         1234        5678        9           33          30          7           756.1234.5678.97

更新

检查了随机数生成器表达式的return值,发现它可以return一个5位数的值,即10000。

SELECT CAST( FLOOR( CAST( 1.0 AS FLOAT ) * ( 9999 - 1000 + 1 ) + 1000 ) AS INT )
-- Result: 10000

要修复它,您需要删除“+ 1”:

SELECT CAST( FLOOR( CAST( 1.0 AS FLOAT ) * ( 9999 - 1000 ) + 1000 ) AS INT )
-- Result: 9999

另一种生成带有控制号的随机 SNIN 的方法。

使用的技巧是交叉应用,用数值计算赔率和偶数的总和。

select concat(code, ctrl) as SNIN
from (values
  (concat('756','.', right(format(rand()*100000,'0000'),4)
               ,'.', right(format(rand()*100000,'0000'),4)
               ,'.', right(format(rand()*100,'0'),1))) 
, ('756.1234.5678.9')
, ('756.1917.1051.7')
) v(code)
cross apply (
  select (10-(sum(odds)+(3*sum(evens)))%10)%10 as ctrl
  from (
    select 
      cast(substring(code_nr,(n*2)+1,1) as int) as odds
    , cast(substring(code_nr,(n+1)*2,1) as int) as evens
    from (values (0),(1),(2),(3),(4),(5)) as nums(n)
    cross join (select replace(code,'.','')) as code(code_nr)
  ) q
) ca;
SNIN
756.9357.6870.03
756.1234.5678.97
756.1917.1051.70

演示 db<>fiddle here