如果您想使用T-SQL生成伪随机的字母数字字符串,应该如何实现?如何从字符串中排除美元符号、破折号和斜杠等字符?
使用 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, 1)
SELECT @LoopCount = @LoopCount + 1
END
我忘了提到使这个程序更加灵活的另一个特性。你可以在@CharPool中重复字符块,从而增加某些字符的权重,使它们更可能被选择。
CONVERT(int, RAND() * @PoolLength) + 1
(注意添加的 +1)。T-SQL 中的 SUBSTRING 从索引 1 开始,因此当前函数有时会添加一个空字符串(当索引为 0 时),而且永远不会添加 @CharPool
中的最后一个字符。 - Dana Cartwright当生成随机数据,特别是用于测试时,使数据既随机又可重现非常有用。秘密在于为随机函数使用明确的种子,这样当使用相同的种子再次运行测试时,它将再次产生完全相同的字符串。以下是一个简化的函数示例,以可重复的方式生成对象名称:
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
在运行测试时,调用者生成一个与测试运行相关联的随机种子(保存在结果表中),然后将种子传递下去,类似于这样:
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:请参见下面的评论,原始过程在推进随机种子的方式上存在问题。我已更新代码,并修复了提到的差一问题。
@seed = rand(@seed+1)*2147483647
。对于值529126,下一个值为1230039262,然后下一个值是192804。在此之后,序列继续以相同的方式延续。输出应该因为第一个字符而不同,但由于 off-by-one 错误,它并没有区别:SUBSTRING(...,0,...)
对于索引0返回空字符串,并且对于529126,这个错误'隐藏'了生成的第一个字符。解决方法是计算 @dice = rand(@seed) * len(@specials)+1
以使索引基于1。 - Remus Rusanurand(@seed+1)
中的 +1
本身成为随机数,并仅从初始种子确定来避免这种情况。这样,即使序列达到相同的值,它们也会立即分歧。 - Remus RusanuSELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9)
如果您正在运行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
我不是T-SQL专家,但我已经使用了最简单的方式:
select char((rand()*25 + 65))+char((rand()*25 + 65))
这将生成两个字符 (A-Z,在ASCII码中为65-90)。
select left(NEWID(),5)
这将返回 guid 字符串的前 5 个字符
Example run
------------
11C89
9DB02
对于一个随机字母,您可以使用:
select substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ',
(abs(checksum(newid())) % 26)+1, 1)
newid()
与rand()
使用的一个重要区别是,如果您返回多行,newid()
会针对每一行分别计算,而rand()
则只计算整个查询一次。
另一个完整字母表的简单解决方案:
SELECT LEFT(REPLACE(REPLACE((SELECT CRYPT_GEN_RANDOM(16) FOR XML PATH(''), BINARY BASE64),'+',''),'/',''),16);
将两个16替换为所需的长度。
示例结果:
pzyMATe3jJwN1XkB
print left(replace(newid(),'-',''),@length) //--@length is the length of random Num.