使用T-SQL生成随机字符串

Sco*_*nce 85 sql t-sql random

如果您想使用T-SQL生成伪随机字母数字字符串,您会怎么做?你会如何排除美元符号,破折号和斜线等字符?

Ste*_*ger 180

使用guid

SELECT @randomString = CONVERT(varchar(255), NEWID())
Run Code Online (Sandbox Code Playgroud)

很短 ...

  • +1,非常容易.与RIGHT/SUBSTRING组合将其截断为所需长度. (6认同)
  • NEWID()中的alpha字符是十六进制的,因此您只能获得AF,而不是字母表的其余部分.从这个意义上说它是有限的. (6认同)
  • 请_not_使用RIGHT/SUBSTRING截断UUID,因为UUID的生成方式既不是唯一也不是随机的! (5认同)
  • 我喜欢这个 - 甜蜜而简单.虽然它没有"可预测性"功能,但对于数据生成非常有用. (2认同)

Chr*_*dge 47

与第一个示例类似,但具有更大的灵活性:

-- min_length = 8, max_length = 12
SET @Length = RAND() * 5 + 8
-- SET @Length = RAND() * (max_length - min_length + 1) + min_length

-- define allowable character explicitly - easy to read this way an easy to 
-- omit easily confused chars like l (ell) and 1 (one) or 0 (zero) and O (oh)
SET @CharPool = 
    'abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ23456789.,-_!$@#%^&*'
SET @PoolLength = Len(@CharPool)

SET @LoopCount = 0
SET @RandomString = ''

WHILE (@LoopCount < @Length) BEGIN
    SELECT @RandomString = @RandomString + 
        SUBSTRING(@Charpool, CONVERT(int, RAND() * @PoolLength), 1)
    SELECT @LoopCount = @LoopCount + 1
END
Run Code Online (Sandbox Code Playgroud)

我忘了提及使其更灵活的其他功能之一.通过重复@CharPool中的字符块,您可以增加某些字符的权重,以便更有可能选择它们.

  • 在SUBSTRING()调用中,此函数存在错误.它应该是`CONVERT(int,RAND()*@PoolLength)+ 1`(注意添加+1).T-SQL中的SUBSTRING从索引1开始,因此它有时会添加一个空字符串(当索引为0时),并且永远不会添加`@ CharPool`中的最后一个字符. (9认同)
  • 好的解决方案 不幸的是,它在udf中不起作用.由于某种原因,它给出了这个错误:"在函数中无效使用副作用运算符'rand'". (2认同)

Rem*_*anu 35

当生成随机数据时,特别是用于测试,使数据随机,但可重现是非常有用的.秘诀是对随机函数使用显式种子,这样当使用相同的种子再次运行测试时,它再次产生完全相同的字符串.以下是以可重现的方式生成对象名称的函数的简化示例:

alter procedure usp_generateIdentifier
    @minLen int = 1
    , @maxLen int = 256
    , @seed int output
    , @string varchar(8000) output
as
begin
    set nocount on;
    declare @length int;
    declare @alpha varchar(8000)
        , @digit varchar(8000)
        , @specials varchar(8000)
        , @first varchar(8000)
    declare @step bigint = rand(@seed) * 2147483647;

    select @alpha = 'qwertyuiopasdfghjklzxcvbnm'
        , @digit = '1234567890'
        , @specials = '_@# '
    select @first = @alpha + '_@';

    set  @seed = (rand((@seed+@step)%2147483647)*2147483647);

    select @length = @minLen + rand(@seed) * (@maxLen-@minLen)
        , @seed = (rand((@seed+@step)%2147483647)*2147483647);

    declare @dice int;
    select @dice = rand(@seed) * len(@first),
        @seed = (rand((@seed+@step)%2147483647)*2147483647);
    select @string = substring(@first, @dice, 1);

    while 0 < @length 
    begin
        select @dice = rand(@seed) * 100
            , @seed = (rand((@seed+@step)%2147483647)*2147483647);
        if (@dice < 10) -- 10% special chars
        begin
            select @dice = rand(@seed) * len(@specials)+1
                , @seed = (rand((@seed+@step)%2147483647)*2147483647);
            select @string = @string + substring(@specials, @dice, 1);
        end
        else if (@dice < 10+10) -- 10% digits
        begin
            select @dice = rand(@seed) * len(@digit)+1
                , @seed = (rand((@seed+@step)%2147483647)*2147483647);
            select @string = @string + substring(@digit, @dice, 1);
        end
        else -- rest 80% alpha
        begin
            declare @preseed int = @seed;
            select @dice = rand(@seed) * len(@alpha)+1
                , @seed = (rand((@seed+@step)%2147483647)*2147483647);

            select @string = @string + substring(@alpha, @dice, 1);
        end

        select @length = @length - 1;   
    end
end
go
Run Code Online (Sandbox Code Playgroud)

运行测试时,调用者会生成一个与测试运行相关联的随机种子(将其保存在结果表中),然后传递种子,类似于:

declare @seed int;
declare @string varchar(256);

select @seed = 1234; -- saved start seed

exec usp_generateIdentifier 
    @seed = @seed output
    , @string = @string output;
print @string;  
exec usp_generateIdentifier 
    @seed = @seed output
    , @string = @string output;
print @string;  
exec usp_generateIdentifier 
    @seed = @seed output
    , @string = @string output;
print @string;  
Run Code Online (Sandbox Code Playgroud)

更新2016-02-17:请参阅下面的评论,原始程序在推进随机种子的方式上存在问题.我更新了代码,并修复了提到的一对一问题.

  • 我知道这是旧线程,但代码返回种子192804和529126的相同字符串 (2认同)
  • @RemusRusanu 我也有兴趣对戴维的评论做出回应 (2认同)
  • 种子按照公式`@seed = rand(@seed+1)*2147483647`前进。对于值 529126,下一个值是 1230039262,然后下一个值是 192804。此后序列以相同方式继续。输出应该因第一个字符而不同,但它不是因为一个错误:`SUBSTRING(..., 0, ...)` 返回索引 0 的空字符串,而对于 529126,它“隐藏”生成的第一个字符。修复是计算`@dice = rand(@seed) * len(@specials)+1`以使索引基于1。 (2认同)

小智 31

使用以下代码返回一个短字符串:

SELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9)
Run Code Online (Sandbox Code Playgroud)

  • 只返回十六进制字符:0-9 &amp; AF (3认同)

小智 19

如果您运行的是SQL Server 2008或更高版本,则可以使用新的加密函数crypt_gen_random(),然后使用base64编码将其设置为字符串.这最多可以使用8000个字符.

declare @BinaryData varbinary(max)
    , @CharacterData varchar(max)
    , @Length int = 2048

set @BinaryData=crypt_gen_random (@Length) 

set @CharacterData=cast('' as xml).value('xs:base64Binary(sql:variable("@BinaryData"))', 'varchar(max)')

print @CharacterData
Run Code Online (Sandbox Code Playgroud)


小智 13

我不是T-SQL的专家,但是我已经使用它的最简单的方法是这样的:

select char((rand()*25 + 65))+char((rand()*25 + 65))
Run Code Online (Sandbox Code Playgroud)

这会生成两个char(AZ,ascii 65-90).


Ham*_*han 10

select left(NEWID(),5)
Run Code Online (Sandbox Code Playgroud)

这将返回guid字符串的最左边5个字符

Example run
------------
11C89
9DB02
Run Code Online (Sandbox Code Playgroud)


Tof*_*net 9

另一个带有完整字母表的简单解决方案:

SELECT LEFT(REPLACE(REPLACE((SELECT CRYPT_GEN_RANDOM(16) FOR XML PATH(''), BINARY BASE64),'+',''),'/',''),16);
Run Code Online (Sandbox Code Playgroud)

将两个 16 替换为所需的长度。

结果示例:

pzyMATe3jJwN1XkB
Run Code Online (Sandbox Code Playgroud)


kru*_*ubo 7

对于一个随机字母,您可以使用:

select substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                 (abs(checksum(newid())) % 26)+1, 1)
Run Code Online (Sandbox Code Playgroud)

usingnewid()和using 之间的一个重要区别rand()是,如果返回多行,newid()则对每一行单独计算,而rand()对整个查询计算一次。


Kri*_*ota 6

这是一个随机的字母数字生成器

print left(replace(newid(),'-',''),@length) //--@length is the length of random Num.
Run Code Online (Sandbox Code Playgroud)

  • 仅返回十六进制字符:0-9 &amp; AF (2认同)

Bri*_*ian 5

这对我有用:我只需要为 ID 生成三个随机字母数字字符,但它可以适用于最多 15 个左右的任意长度。

declare @DesiredLength as int = 3;
select substring(replace(newID(),'-',''),cast(RAND()*(31-@DesiredLength) as int),@DesiredLength);
Run Code Online (Sandbox Code Playgroud)


def*_*mer 5

有很多好的答案,但到目前为止,没有一个允许可自定义的字符池并用作列的默认值。我希望能够做这样的事情:

alter table MY_TABLE add MY_COLUMN char(20) not null
  default dbo.GenerateToken(crypt_gen_random(20))
Run Code Online (Sandbox Code Playgroud)

所以我想出了这个。如果您修改它,请注意硬编码数字 32。

-- Converts a varbinary of length N into a varchar of length N.
-- Recommend passing in the result of CRYPT_GEN_RANDOM(N).
create function GenerateToken(@randomBytes varbinary(max))
returns varchar(max) as begin

-- Limit to 32 chars to get an even distribution (because 32 divides 256) with easy math.
declare @allowedChars char(32);
set @allowedChars = 'abcdefghijklmnopqrstuvwxyz012345';

declare @oneByte tinyint;
declare @oneChar char(1);
declare @index int;
declare @token varchar(max);

set @index = 0;
set @token = '';

while @index < datalength(@randomBytes)
begin
    -- Get next byte, use it to index into @allowedChars, and append to @token.
    -- Note: substring is 1-based.
    set @index = @index + 1;
    select @oneByte = convert(tinyint, substring(@randomBytes, @index, 1));
    select @oneChar = substring(@allowedChars, 1 + (@oneByte % 32), 1); -- 32 is the number of @allowedChars
    select @token = @token + @oneChar;
end

return @token;

end
Run Code Online (Sandbox Code Playgroud)