在 SQL Server 中生成看似随机的唯一数字 ID

本文关键字:随机 唯一 数字 ID Server SQL | 更新日期: 2023-09-27 18:30:34

我需要使用 SQL Server 生成看似随机的唯一 8 位数字 ID(可以在前面填充零)。是否有内置功能?我看到了这个Identity属性,但它是顺序的,而不是随机的。

如果这是不可能的,直接将随机生成的 ID 写入 db 然后检查异常是否是一种好的做法?(请注意,我的应用程序是多线程的,因此在编写之前进行检查并不能保证唯一性,除非在原子操作中完成。

谢谢!

更新:添加了"数字"以澄清。经过编辑以表明随机性不需要加密强度或任何接近的东西。只是看似随机就足够了。奥利弗提出了一个优雅的解决方案,我已经用这种方法发布了一个答案。谢谢,奥利弗!

在 SQL Server 中生成看似随机的唯一数字 ID

随机性与唯一性冲突,但当数字只需要随机出现时,@Oliver提出了一个优雅的解决方案,而存在底层顺序。根据 Erics http://ericlippert.com/2013/11/14/a-practical-use-of-multiplicative-inverses/,主要思想是,对于给定一对互质正整数 x 和 m,我们可以找到一个乘法逆 y,其中 (x*y) % m == 1。这非常有用,因为给定一个数据库行 ID z,我们可以通过执行 encoded = (z*x) % m 将 z 映射到另一个整数。现在给定这个encoded,我们如何才能让z回来?简单,z = (encoded * y) % m,因为(x*y*z) % m == z给定 z <m。这种一对一的对应关系保证了"编码"的唯一性,同时提供了随机性。>

请注意,Eric 展示了如何计算这个乘法逆。但如果你懒惰,就有这个。

在我的实现中,我只是按原样存储每一行的顺序 ID。然后,每个 ID 都映射到另一个号码,类似于文章中的"发票号码"。当客户将这个"发票编号"交还给您时,您可以使用乘法逆将其映射回其原始数据库 ID。

下面是从 0 到 9 的编码和解码序列的 C# 示例。

public static void SeeminglyRandomSequence()
{   //use long to prevent overflow
    long m = 10; //modulo, choose m to be much larger than number of rows
    long x = 7; //anything coprime to m
    long y = 3; //multiplicative inverse of x, where (y*x) % m == 1
    List<long> encodedSequence = new List<long>();
    List<long> decodedSequence = new List<long>();
    for (long i = 0; i < m; i++)
    {
        long encoded = (i * x) % m;
        encodedSequence.Add(encoded);
    }
    foreach (long encoded in encodedSequence)
    {
        long decoded = (encoded * y) % m;
        decodedSequence.Add(decoded);
    }
    Debug.WriteLine("just encoded sequence from 0 to {0}. Result shown below:", (m - 1));
    Debug.WriteLine("encoded sequence: " + string.Join(" ", encodedSequence));
    Debug.WriteLine("decoded sequence: " + string.Join(" ", decodedSequence));
}

打印结果为:

just encoded sequence from 0 to 9. Result shown below:
encoded sequence: 0 7 4 1 8 5 2 9 6 3
decoded sequence: 0 1 2 3 4 5 6 7 8 9

如您所见,每个输入都映射到一个唯一的输出,并且很容易反转此映射。在应用程序中,您可能希望从 1 开始,因为 0 始终映射到自身。

为了显示较大 m 的"表观随机性",以下是 m=100,000,000 时的前 10 个映射:

just encoded sequence from 1 to 10. Result shown below:
encoded sequence: 81654327 63308654 44962981 26617308 8271635 89925962 71580289 53234616 34888943 16543270
decoded sequence: 1 2 3 4 5 6 7 8 9 10
使用以下

查询创建 8 位 randow 唯一编号。

SELECT CAST(RAND() * 100000000 AS INT) AS [RandomNumber]

为避免在将现有数字插入数据库时出现异常,请使用以下查询。

IF NOT EXIST(SELECT UniqueColumnID FROM TABLENAME WHERE UniqueColumnID = @RandowNumber)
BEGIN
    --Insert query using  @RandowNumber.
END

您可以使用 NEWID() 生成每次始终随机且唯一的 uniqueIdentifier 数据

要获得 8 个字符,您可以使用子字符串、左等函数。

select substring( cast( NEWID() as varchar(100)),0,8)

或唯一性的新逻辑:- http://forums.asp.net/t/1474299.aspx?How+to+generate+unique+key+of+fixed+length+20+digit+in+sql+server+2005+

select Left(NewID(),4)+Right(NewId(),4)

您也可以为此使用 random() 函数。

检查此链接:如何为 TSQL 选择中的每一行生成随机数?

如何在SQL Server中获取数字随机唯一ID

更新如果要唯一值 int 数据类型和 8 个字符长。适合制作如下所示的标识列,适用于 8 个字符长度的数据均值 (10,000,000)。但在那之后,它会给你例外。所以要小心你想要的逻辑。(我还是说,这是个坏主意)。存储为上述随机值,长度更长,因此具有唯一性。

create table temp (id numeric(8,0) IDENTITY(1,1) NOT NULL, value1 varchar ) --if you want do not stop after 8 character , give int datatype.
insert into temp values( 'a'), ('b'), ('c')
select * from temp
drop table temp

最后

它不能保证唯一,但很难用 NEWID() 获得重复项(请参阅上面的链接 forums.asp.net)

创建一个 SQL 函数或过程,如下所示:

ALTER FUNCTION [dbo].[GenerateRandomNo] 
(
@Lower INT  = 111111111,
@Upper INT = 999999999
)
RETURNS NVARCHAR(128)
AS
BEGIN
DECLARE @TempRandom FLOAT
DECLARE @Random NVARCHAR(128); 

-- Add the T-SQL statements to compute the return value here
SELECT @TempRandom = RandomNo from RandomNo
SELECT @Random = CONVERT(NVARCHAR(128),CONVERT(INT,ROUND(((@Upper - @Lower -1) * @TempRandom + @Lower), 0)))
WHILE EXISTS(SELECT * FROM Table WHERE Column = @Random)
BEGIN
    SELECT @TempRandom = RandomNo from RandomNo
    SELECT @Random = CONVERT(NVARCHAR(128),CONVERT(INT, ROUND(((@Upper - @Lower -1) * @TempRandom + @Lower), 0)))
END
-- Return the result of the function
RETURN @Random
END

然后调用该函数传递参数,如果你想生成具有特定长度或范围的随机否。

 --create
--   table Tbl( idx int)
   DECLARE
   @unique_id int  
 SET @unique_id=  ( SELECT ROUND( 89999999 * RAND(
                                                   ) + 10000000 , 0
                                  )
                    )            
IF not EXISTS( SELECT idx
             FROM tbl
             WHERE idx = @unique_id
         )
    BEGIN
        INSERT INTO  tbl( idx
                               )
        VALUES( @unique_id
              )
SELECT @unique_id, * FROM tbl
    END;
    --TRUNCATE TABLE tbl