用T-SQL生成随机string

如果你想用T-SQL生成一个伪随机的字母数字string,你会怎么做? 你会如何排除像美元符号,破折号和斜杠字符?

当生成随机数据,特别是testing时,使数据随机,但可重现是非常有用的。 秘密就是为随机函数使用明确的种子,这样当再次用相同的种子运行testing时,它会再次产生完全相同的string。 下面是一个以可重现的方式生成对象名称的函数的简化示例:

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 

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

 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; 

更新2016-02-17:请参阅下面的注释,原始程序在提前随机种子方面存在问题。 我更新了代码,并修复了提到的一个问题。

使用guid

 SELECT @randomString = CONVERT(varchar(255), NEWID()) 

很短 …

类似于第一个例子,但具有更多的灵活性:

 -- 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 

我忘了提到使其更灵活的其他function之一。 通过重复@CharPool中的字符块,可以增加某些字符的权重,以便更有可能select它们。

使用下面的代码来返回一个简短的string:

 SELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9) 

如果您正在运行SQL Server 2008或更高版本,则可以使用新的encryption函数crypt_gen_random(),然后使用base64编码将其设置为string。 这将工作多达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 
 select left(NEWID(),5) 

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

 Example run ------------ 11C89 9DB02 

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

 print left(replace(newid(),'-',''),@length) //--@length is the length of random Num. 

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

 select char((rand()*25 + 65))+char((rand()*25 + 65)) 

这会生成两个字符(AZ,ASCII码为65-90)。

这对我有用:我需要为一个ID生成三个随机字母数字字符,但它可以适用于任何长度高达15左右。

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

我意识到这是一个老问题,有很多好的答案。 但是,当我发现这一点,我也发现了由Saeid Hasani在TechNet上的最新文章

T-SQL:如何生成随机密码

虽然解决scheme侧重于密码,但适用于一般情况。 Saeid通过各种考虑来解决问题。 这是非常有益的。

包含所有代码块的脚本可以通过TechNet Gallery单独获得,但我肯定会从文章开始。

我使用我开发的这个程序只是简单的规定你想要在inputvariables中显示的字符,你也可以定义长度。 希望这个格式好,我是新来堆栈溢出。

 IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND object_id = OBJECT_ID(N'GenerateARandomString')) DROP PROCEDURE GenerateARandomString GO CREATE PROCEDURE GenerateARandomString ( @DESIREDLENGTH INTEGER = 100, @NUMBERS VARCHAR(50) = '0123456789', @ALPHABET VARCHAR(100) ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', @SPECIALS VARCHAR(50) = '_=+-$£%^&*()"!@~#:', @RANDOMSTRING VARCHAR(8000) OUT ) AS BEGIN -- Author David Riley -- Version 1.0 -- You could alter to one big string .ee numebrs , alpha special etc -- added for more felxibility in case I want to extend ie put logic in for 3 numbers, 2 pecials 3 numbers etc -- for now just randomly pick one of them DECLARE @SWAP VARCHAR(8000); -- Will be used as a tempoary buffer DECLARE @SELECTOR INTEGER = 0; DECLARE @CURRENTLENGHT INTEGER = 0; WHILE @CURRENTLENGHT < @DESIREDLENGTH BEGIN -- Do we want a number, special character or Alphabet Randonly decide? SET @SELECTOR = CAST(ABS(CHECKSUM(NEWID())) % 3 AS INTEGER); -- Always three 1 number , 2 alphaBET , 3 special; IF @SELECTOR = 0 BEGIN SET @SELECTOR = 3 END; -- SET SWAP VARIABLE AS DESIRED SELECT @SWAP = CASE WHEN @SELECTOR = 1 THEN @NUMBERS WHEN @SELECTOR = 2 THEN @ALPHABET ELSE @SPECIALS END; -- MAKE THE SELECTION SET @SELECTOR = CAST(ABS(CHECKSUM(NEWID())) % LEN(@SWAP) AS INTEGER); IF @SELECTOR = 0 BEGIN SET @SELECTOR = LEN(@SWAP) END; SET @RANDOMSTRING = ISNULL(@RANDOMSTRING,'') + SUBSTRING(@SWAP,@SELECTOR,1); SET @CURRENTLENGHT = LEN(@RANDOMSTRING); END; END; GO DECLARE @RANDOMSTRING VARCHAR(8000) EXEC GenerateARandomString @RANDOMSTRING = @RANDOMSTRING OUT SELECT @RANDOMSTRING 

我第一次遇到这个博客文章 ,然后想出了以下存储过程,我正在使用一个当前的项目(对不起奇怪的格式):

 CREATE PROCEDURE [dbo].[SpGenerateRandomString] @sLength tinyint = 10, @randomString varchar(50) OUTPUT AS BEGIN SET NOCOUNT ON DECLARE @counter tinyint DECLARE @nextChar char(1) SET @counter = 1 SET @randomString = ” WHILE @counter <= @sLength BEGIN SELECT @nextChar = CHAR(48 + CONVERT(INT, (122-48+1)*RAND())) IF ASCII(@nextChar) not in (58,59,60,61,62,63,64,91,92,93,94,95,96) BEGIN SELECT @randomString = @randomString + @nextChar SET @counter = @counter + 1 END END END 

我在SQL 2000中通过创build一个我想要使用的字符的表来创build一个视图,该视图从newid()中select该表中的字符,然后从该视图中select前1个字符。

 CREATE VIEW dbo.vwCodeCharRandom AS SELECT TOP 100 PERCENT CodeChar FROM dbo.tblCharacter ORDER BY NEWID() ... SELECT TOP 1 CodeChar FROM dbo.vwCodeCharRandom 

然后,您可以简单地从视图中拉出字符,并根据需要连接它们。

编辑:斯蒂芬的回应启发…

 select top 1 RandomChar from tblRandomCharacters order by newid() 

不需要一个观点(事实上,我不确定为什么我这样做了 – 几年前的代码)。 您仍然可以指定要在表格中使用的字符。

inheritance人基于新的Id。

 with list as ( select 1 as id,newid() as val union all select id + 1,NEWID() from list where id + 1 < 10 ) select ID,val from list option (maxrecursion 0) 

我认为我会分享,或者回馈给社区…这是基于ASCII的,解决scheme并不完美,但是效果很好。 享受,戈兰B.

 /* -- predictable masking of ascii chars within a given decimal range -- purpose: -- i needed an alternative to hashing alg. or uniqueidentifier functions -- because i wanted to be able to revert to original char set if possible ("if", the operative word) -- notes: wrap below in a scalar function if desired (ie recommended) -- by goran biljetina (2014-02-25) */ declare @length int ,@position int ,@maskedString varchar(500) ,@inpString varchar(500) ,@offsetAsciiUp1 smallint ,@offsetAsciiDown1 smallint ,@ipOffset smallint ,@asciiHiBound smallint ,@asciiLoBound smallint set @ipOffset=null set @offsetAsciiUp1=1 set @offsetAsciiDown1=-1 set @asciiHiBound=126 --> up to and NOT including set @asciiLoBound=31 --> up from and NOT including SET @inpString = '{"config":"some string value", "boolAttr": true}' SET @length = LEN(@inpString) SET @position = 1 SET @maskedString = '' --> MASK: --------- WHILE (@position < @length+1) BEGIN SELECT @maskedString = @maskedString + ISNULL( CASE WHEN ASCII(SUBSTRING(@inpString,@position,1))>@asciiLoBound AND ASCII(SUBSTRING(@inpString,@position,1))<@asciiHiBound THEN CHAR(ASCII(SUBSTRING(@inpString,@position,1))+ (case when @ipOffset is null then case when ASCII(SUBSTRING(@inpString,@position,1))%2=0 then @offsetAsciiUp1 else @offsetAsciiDown1 end else @ipOffset end)) WHEN ASCII(SUBSTRING(@inpString,@position,1))<=@asciiLoBound THEN '('+CONVERT(varchar,ASCII(SUBSTRING(@Inpstring,@position,1))+1000)+')' --> wrap for decode WHEN ASCII(SUBSTRING(@inpString,@position,1))>=@asciiHiBound THEN '('+CONVERT(varchar,ASCII(SUBSTRING(@inpString,@position,1))+1000)+')' --> wrap for decode END ,'') SELECT @position = @position + 1 END select @MaskedString SET @inpString = @maskedString SET @length = LEN(@inpString) SET @position = 1 SET @maskedString = '' --> UNMASK (Limited to within ascii lo-hi bound): ------------------------------------------------- WHILE (@position < @length+1) BEGIN SELECT @maskedString = @maskedString + ISNULL( CASE WHEN ASCII(SUBSTRING(@inpString,@position,1))>@asciiLoBound AND ASCII(SUBSTRING(@inpString,@position,1))<@asciiHiBound THEN CHAR(ASCII(SUBSTRING(@inpString,@position,1))+ (case when @ipOffset is null then case when ASCII(SUBSTRING(@inpString,@position,1))%2=1 then @offsetAsciiDown1 else @offsetAsciiUp1 end else @ipOffset*(-1) end)) ELSE '' END ,'') SELECT @position = @position + 1 END select @maskedString 

这使用兰德与其他答案之一的种子,但没有必要在每个电话提供种子。 在第一个电话上提供就足够了。

这是我修改的代码。

 IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND object_id = OBJECT_ID(N'usp_generateIdentifier')) DROP PROCEDURE usp_generateIdentifier GO create 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) select @alpha = 'qwertyuiopasdfghjklzxcvbnm' , @digit = '1234567890' , @specials = '_@#$&' select @first = @alpha + '_@'; -- Establish our rand seed and store a new seed for next time set @seed = (rand(@seed)*2147483647); select @length = @minLen + rand() * (@maxLen-@minLen); --print @length declare @dice int; select @dice = rand() * len(@first); select @string = substring(@first, @dice, 1); while 0 < @length begin select @dice = rand() * 100; if (@dice < 10) -- 10% special chars begin select @dice = rand() * len(@specials)+1; select @string = @string + substring(@specials, @dice, 1); end else if (@dice < 10+10) -- 10% digits begin select @dice = rand() * len(@digit)+1; select @string = @string + substring(@digit, @dice, 1); end else -- rest 80% alpha begin select @dice = rand() * len(@alpha)+1; select @string = @string + substring(@alpha, @dice, 1); end select @length = @length - 1; end end go 

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

 alter table MY_TABLE add MY_COLUMN char(20) not null default dbo.GenerateToken(crypt_gen_random(20)) 

所以我想出了这个。 如果您修改它,请小心硬编码的数字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 

有时,我们需要很多随机的东西:爱,善良,度假等。多年来,我收集了几个随机生成器,这些是来自Pinal Dave和我发现一次的stackoverflow答案。 以下参考。

 --Adapted from Pinal Dave; http://blog.sqlauthority.com/2007/04/29/sql-server-random-number-generator-script-sql-query/ SELECT ABS( CAST( NEWID() AS BINARY( 6)) %1000) + 1 AS RandomInt , CAST( (ABS( CAST( NEWID() AS BINARY( 6)) %1000) + 1)/7.0123 AS NUMERIC( 15,4)) AS RandomNumeric , DATEADD( DAY, -1*(ABS( CAST( NEWID() AS BINARY( 6)) %1000) + 1), GETDATE()) AS RandomDate --This line from http://stackoverflow.com/questions/15038311/sql-password-generator-8-characters-upper-and-lower-and-include-a-number , CAST((ABS(CHECKSUM(NEWID()))%10) AS VARCHAR(1)) + CHAR(ASCII('a')+(ABS(CHECKSUM(NEWID()))%25)) + CHAR(ASCII('A')+(ABS(CHECKSUM(NEWID()))%25)) + LEFT(NEWID(),5) AS RandomChar , ABS(CHECKSUM(NEWID()))%50000+1 AS RandomID 

SQL Server 2012+中,我们可以连接某些(G)UID的二进制文件,然后对结果执行base64转换。

 SELECT textLen.textLen , left(( select CAST(newid() as varbinary(max)) + CAST(newid() as varbinary(max)) where textLen.textLen is not null /*force evaluation for each outer query row*/ FOR XML PATH(''), BINARY BASE64 ),textLen.textLen) as randomText FROM ( values (2),(4),(48) ) as textLen(textLen) --define lengths here ; 

如果您需要更长的string(或者您在结果中看到=字符),则需要在子select中添加更多+ CAST(newid() as varbinary(max))

所以我喜欢上面的很多答案,但是我正在寻找一些更加随机的东西。 我也想要一种明确地调出排除字符的方法。 下面是我的解决scheme,使用调用CRYPT_GEN_RANDOM来获得密码随机数的视图。 在我的例子中,我只select了一个8字节的随机数字。 请注意,你可以增加这个大小,也可以利用函数的种子参数。 以下是该文档的链接: https : //docs.microsoft.com/en-us/sql/t-sql/functions/crypt-gen-random-transact-sql

 CREATE VIEW [dbo].[VW_CRYPT_GEN_RANDOM_8] AS SELECT CRYPT_GEN_RANDOM(8) as [value]; 

创build视图的原因是因为CRYPT_GEN_RANDOM不能直接从函数调用。

从那里,我创build了一个标量函数,它接受一个长度和一个string参数,该参数可以包含逗号分隔的string。

 CREATE FUNCTION [dbo].[fn_GenerateRandomString] ( @length INT, @excludedCharacters VARCHAR(200) --Comma delimited string of excluded characters ) RETURNS VARCHAR(Max) BEGIN DECLARE @returnValue VARCHAR(Max) = '' , @asciiValue INT , @currentCharacter CHAR; --Optional concept, you can add default excluded characters SET @excludedCharacters = CONCAT(@excludedCharacters,',^,*,(,),-,_,=,+,[,{,],},\,|,;,:,'',",<,.,>,/,`,~'); --Table of excluded characters DECLARE @excludedCharactersTable table([asciiValue] INT); --Insert comma INSERT INTO @excludedCharactersTable SELECT 44; --Stores the ascii value of the excluded characters in the table INSERT INTO @excludedCharactersTable SELECT ASCII(TRIM(value)) FROM STRING_SPLIT(@excludedCharacters, ',') WHERE LEN(TRIM(value)) = 1; --Keep looping until the return string is filled WHILE(LEN(@returnValue) < @length) BEGIN --Get a truly random integer values from 33-126 SET @asciiValue = (SELECT TOP 1 (ABS(CONVERT(INT, [value])) % 94) + 33 FROM [dbo].[VW_CRYPT_GEN_RANDOM_8]); --If the random integer value is not in the excluded characters table then append to the return string IF(NOT EXISTS(SELECT * FROM @excludedCharactersTable WHERE [asciiValue] = @asciiValue)) BEGIN SET @returnValue = @returnValue + CHAR(@asciiValue); END END RETURN(@returnValue); END 

下面是如何调用函数的一个例子。

 SELECT [dbo].[fn_GenerateRandomString](8,'!,@,#,$,%,&,?'); 

〜干杯