【问题标题】:T-SQL :: generate random Swiss National Identification Number (AHV/AVS)T-SQL :: 生成随机瑞士国民身份证号 (AHV/AVS)
【发布时间】:2022-01-17 15:23:21
【问题描述】:

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

我找到了一个can do that 的网站,如果我查看同一页面的源代码,我也可以看到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

===>这就是我们最终获得7的方式,这是最后一个数字

我创建了一个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? 
)

此代码会生成我需要的所有随机数,但我不知道如何从两个数字中选择一个。

不知道T-SQL能否解析数字,二选一。

【问题讨论】:

  • “从第一个数字开始,从两个数字中取一个数字并将它们相加:7 + 6 + 2 + 4 + 6 + 8 = 33” 嗯?取一个数并求和?那将是数字本身。 SUM(7) = 7如果取一个数字,为​​什么表达式有6个数字?我不遵循这个逻辑。
  • 对不起,我的英语很差...你必须总结first, third, fifth, seventh, nineth, eleven
  • 你的意思是像“从第一个数字开始,每隔一个数字(第一个、第三个、第五个等)然后SUM他们。”
  • 然后second, forth, sixth, eight, ten, twelve
  • 是的@Larnu,其他的号码就是我要找的,我要编辑

标签: javascript sql sql-server tsql random


【解决方案1】:

首先,您当前的逻辑有缺陷,您得到一个介于 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。

【讨论】:

  • 谢谢@Larnu,我将使用它来匿名化数据库。我需要匿名化包含 AVS 编号的列。
  • 请注意,最后一位计算有时会生成“10”。虽然这在您的规则中没有说明,但我相信您需要 %10 最终结果才能将 10 转换为 0。
  • Larnu, re " 首先,你当前的逻辑是有缺陷的,你得到一个 0 到 9999 之间的随机数,但是你没有使这个值固定宽度。 - 你误会了。他使用的公式是RND * (9999-1000+1)+1000),所以能返回的最小数是1000
【解决方案2】:

另一种生成带有控制号的随机 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

dbfiddle here

上的演示

【讨论】:

  • 感谢@LukStorms 的努力,但它最后返回了 3 位数字:snipboard.io/sBN6va.jpg
  • @FrancescoMantovani 实际上,它需要一个额外的模数。因为 10-0=10。谢谢。
【解决方案3】:

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

注意:我在校验位计算的最终结果中添加了 %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

更新

查看随机数生成器表达式的返回值,发现它可以返回一个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

【讨论】:

  • 哦,那也很好,谢谢亚历克斯。我喜欢你的方法,一次生成随机数一。
  • 我不明白为什么它会创建 2 行而不是 1 行
  • 我使用了您原始示例中的值:“(756, 1234, 5678, 9)”。你可以删除它。
  • 随机数是否包含小于 1000 的值? CAST( FLOOR( RAND( CHECKSUM( NEWID())) * ( 9999 - 1000 + 1 ) + 1000 ) AS INT ) 不会生成它们。
  • RAND 函数返回 0 到 1 之间的数字。公式为 (RND( 0 到 1 ) * 9000 ) + 1000。最小的数字(当 RND 为 0 时)为 1000。最大的(刚刚检查可以是1000)。相应地更新了我的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-22
  • 2014-08-06
  • 2021-02-16
相关资源
最近更新 更多