Sql server 如何生成具有指定长度的随机字母数字唯一字符

Sql server 如何生成具有指定长度的随机字母数字唯一字符,sql-server,tsql,Sql Server,Tsql,问题描述如下: 生成唯一的字母数字字符 字符长度应为32 唯一编号可以在当前时间内进行播种,以帮助生成编号的唯一性 字母表字符必须来自此池:abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz 样本输出:445rpxlKYPkj1pg4q8nAy7Ab91zxZ8v1 我可以使用Java来实现这一点,但如果您能在MS SQL或T-SQL上帮助我实现这一点,我将不胜感激。首先,您需要将字符串拆分为单独的行。然后,对随机排序执行选择和orderbyne

问题描述如下:

  • 生成唯一的字母数字字符
  • 字符长度应为32
  • 唯一编号可以在当前时间内进行播种,以帮助生成编号的唯一性
  • 字母表字符必须来自此池:
    abcdefghijklmnopqrstuvxyzabcdefghijklmnopqrstuvxyz
  • 样本输出:
    445rpxlKYPkj1pg4q8nAy7Ab91zxZ8v1


    我可以使用Java来实现这一点,但如果您能在MS SQL或T-SQL上帮助我实现这一点,我将不胜感激。

    首先,您需要将字符串拆分为单独的行。然后,对随机排序执行
    选择
    orderbynewid()
    。最后,对XML路径(“”)使用
    将它们连接回:

    DECLARE @str VARCHAR(100) = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    
    ;WITH E1(N) AS( -- 10 ^ 1 = 10 rows
        SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)
    ),
    E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
    E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
    CteTally(N) AS(
        SELECT TOP(LEN(@str)) ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
        FROM E4
    )
    SELECT  (
        SELECT TOP(32)
            SUBSTRING(@str, N, 1)
        FROM CteTally t
        ORDER BY NEWID()
        FOR XML PATH('')
    ) AS Result
    

    以上是一个通用的随机字符串生成器。您可以修改它以满足您的需要。如果需求不会改变,您可以简单地使用:

    DECLARE @str VARCHAR(100) = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    ;WITH E1(N) AS( -- 52 Rows
        SELECT 1 FROM( VALUES
            (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),
            (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),
            (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),
            (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),
            (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),
            (1),(1)
        )t(N)
    ),
    CteTally(N) AS(
        SELECT ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
        FROM E1
    )
    SELECT  (
        SELECT TOP(32)
            SUBSTRING(@str, N, 1)
        FROM CteTally t
        ORDER BY NEWID()
        FOR XML PATH('')
    ) AS Result
    

    首先,需要将字符串拆分为单独的行。然后,对随机排序执行
    选择
    orderbynewid()
    。最后,对XML路径(“”)使用
    将它们连接回:

    DECLARE @str VARCHAR(100) = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    
    ;WITH E1(N) AS( -- 10 ^ 1 = 10 rows
        SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)
    ),
    E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
    E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
    CteTally(N) AS(
        SELECT TOP(LEN(@str)) ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
        FROM E4
    )
    SELECT  (
        SELECT TOP(32)
            SUBSTRING(@str, N, 1)
        FROM CteTally t
        ORDER BY NEWID()
        FOR XML PATH('')
    ) AS Result
    

    以上是一个通用的随机字符串生成器。您可以修改它以满足您的需要。如果需求不会改变,您可以简单地使用:

    DECLARE @str VARCHAR(100) = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    ;WITH E1(N) AS( -- 52 Rows
        SELECT 1 FROM( VALUES
            (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),
            (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),
            (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),
            (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),
            (1),(1),(1),(1),(1),(1),(1),(1),(1),(1),
            (1),(1)
        )t(N)
    ),
    CteTally(N) AS(
        SELECT ROW_NUMBER() OVER(ORDER BY(SELECT NULL))
        FROM E1
    )
    SELECT  (
        SELECT TOP(32)
            SUBSTRING(@str, N, 1)
        FROM CteTally t
        ORDER BY NEWID()
        FOR XML PATH('')
    ) AS Result
    

    我使它足够通用,可以处理任何字符池和任何输出长度。其核心思想是获取一个随机的字节序列,并使用基本转换算法将一个长数字转换为一个新的表示形式,然后使用所需的字符作为其“数字”转换为字符串

    对于您的特定场景,我们需要大约183位,或log2(52)x 32,以达到您所需的长度。使用
    newid()
    将生成唯一的位序列,但一次只能生成128位,一系列值将简单地连接起来,直到有足够的位序列为止。然后有一个值可以操作,主循环本质上就是我们从小学学到的长除法。中间计算保留在
    varbinary
    数组中,循环仅在获得足够的输出字符之前继续。每次迭代确定新基中的另一个低阶数字,这可以提前终止,因为它们不会改变。如果输出不使用至少一个
    newid()
    ,则该算法无法保证任何全局唯一性,因此请确保log2(len(pool))x输出长度至少为128

    目标基数(最终是字符池的长度)不能超过256。通过设置
    @e
    的128字节最大长度,我硬编码了一个限制。对于问题
    @e
    只需要32字节长,可以根据需要向上或向下调整,也可以定义为
    varbinary(max)
    。如果您需要更真实的随机性,您可以找到熵位的另一个来源,如
    crypt\u gen\u random()
    。由于唯一性似乎是主要关注点,因此该答案符合该要求。顺便说一下,池中重复的角色自然会为碰撞打开大门

    这是快速和通用的,可以很容易地包装在一个函数中。更健壮的实现将处理这些额外的检查

    declare @characterPool varchar(256) =
        'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    declare @outputLength int = 32;
    
    declare @n int = 0; /* counter */
    declare @numLoops int = ceiling(log(len(@characterPool)) / log(2) * @outputLength / 128)
    declare @e varbinary(128) = 0x; /* entropy */
    
    while @n < @numLoops
    begin
        set @e = cast(newid() as binary(16)); set @n += 1;
    end
    
    declare @b int; /* byte */
    declare @d int; /* dividend */
    declare @out varchar(128) = '';
    
    declare @outputBase int = len(@characterPool);
    declare @entropyBytes int = len(@e);
    
    declare @m int = 0;
    while @m < @outputLength
    begin
        set @b = 0; set @d = 0; set @n = 0;
        while @n < @entropyBytes /* big-endian */
        begin
            set @b = (@b - @d * @outputBase) * 256 + cast(substring(@e, @n + 1, 1) as int);
            set @d = @b / @outputBase;
            set @e = cast(stuff(@e, @n + 1, 1, cast(@d as binary(1))) as varbinary(128));
            set @n += 1;
        end
        set @out = substring(@characterPool, @b - @d * @outputBase + 1, 1) + @out;
        set @m += 1;
    end
    
    select @out as "UniqueString"
    

    我使它足够通用,可以处理任何字符池和任何输出长度。其核心思想是获取一个随机的字节序列,并使用基本转换算法将一个长数字转换为一个新的表示形式,然后使用所需的字符作为其“数字”转换为字符串

    对于您的特定场景,我们需要大约183位,或log2(52)x 32,以达到您所需的长度。使用
    newid()
    将生成唯一的位序列,但一次只能生成128位,一系列值将简单地连接起来,直到有足够的位序列为止。然后有一个值可以操作,主循环本质上就是我们从小学学到的长除法。中间计算保留在
    varbinary
    数组中,循环仅在获得足够的输出字符之前继续。每次迭代确定新基中的另一个低阶数字,这可以提前终止,因为它们不会改变。如果输出不使用至少一个
    newid()
    ,则该算法无法保证任何全局唯一性,因此请确保log2(len(pool))x输出长度至少为128

    目标基数(最终是字符池的长度)不能超过256。通过设置
    @e
    的128字节最大长度,我硬编码了一个限制。对于问题
    @e
    只需要32字节长,可以根据需要向上或向下调整,也可以定义为
    varbinary(max)
    。如果您需要更真实的随机性,您可以找到熵位的另一个来源,如
    crypt\u gen\u random()
    。由于唯一性似乎是主要关注点,因此该答案符合该要求。顺便说一下,池中重复的角色自然会为碰撞打开大门

    这是快速和通用的,可以很容易地包装在一个函数中。更健壮的实现将处理这些额外的检查

    declare @characterPool varchar(256) =
        'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    declare @outputLength int = 32;
    
    declare @n int = 0; /* counter */
    declare @numLoops int = ceiling(log(len(@characterPool)) / log(2) * @outputLength / 128)
    declare @e varbinary(128) = 0x; /* entropy */
    
    while @n < @numLoops
    begin
        set @e = cast(newid() as binary(16)); set @n += 1;
    end
    
    declare @b int; /* byte */
    declare @d int; /* dividend */
    declare @out varchar(128) = '';
    
    declare @outputBase int = len(@characterPool);
    declare @entropyBytes int = len(@e);
    
    declare @m int = 0;
    while @m < @outputLength
    begin
        set @b = 0; set @d = 0; set @n = 0;
        while @n < @entropyBytes /* big-endian */
        begin
            set @b = (@b - @d * @outputBase) * 256 + cast(substring(@e, @n + 1, 1) as int);
            set @d = @b / @outputBase;
            set @e = cast(stuff(@e, @n + 1, 1, cast(@d as binary(1))) as varbinary(128));
            set @n += 1;
        end
        set @out = substring(@characterPool, @b - @d * @outputBase + 1, 1) + @out;
        set @m += 1;
    end
    
    select @out as "UniqueString"
    

    有一个函数可以生成给定数量的随机字节,最多8000个。无需在循环中调用
    NEWID
    。@Vladimir我在关于唯一性的评论中提到了这一点。无论哪种方式,冲突的可能性都很低,但是使用
    newid()
    的原因是为了保证128位是全局唯一的,不管这最终是一个真正的问题。我明白了。我没有仔细阅读这个问题。我没有考虑唯一性,我关注的是随机性。伟大的解决方案@shawnt00将尝试它。谢谢。有一个函数可以生成给定数量的随机字节,最多8000个。有