如果要使用T-SQL生成伪随机的字母数字字符串,您将如何做?您如何从中排除美元符号,破折号和斜线之类的字符?
当生成随机数据(特别是用于测试)时,使数据随机但可重现非常有用。秘诀是为随机函数使用显式种子,这样当再次使用相同种子运行测试时,它将再次产生完全相同的字符串。这是一个函数的简化示例,该函数以可重现的方式生成对象名称:
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年2月17日更新:请参阅下面的评论,原始过程在推进随机种子的方式方面存在问题。我更新了代码,并修复了上述问题。
请注意,在我的示例中,重新播种主要是为了阐明这一点。实际上,只要随后的呼叫顺序是确定性的,它足以为每个会话播种RNG。
我知道这是旧线程,但是代码为种子192804和529126返回相同的字符串
@RemusRusanu我也将对回应davey的评论感兴趣
种子按公式前进
@seed = rand(@seed+1)*2147483647
。对于值529126,下一个值是1230039262,然后下一个值是192804。此后,序列相同地继续。输出的第一个字符应该有所不同,但这不是因为一个错误的错误:SUBSTRING(..., 0, ...)
返回索引0的空字符串,对于529126,此字符串“隐藏”生成的第一个字符。解决方法是计算@dice = rand(@seed) * len(@specials)+1
以使索引为1。该问题源于以下事实:随机序列一旦达到共同的值,它们的进展就相同。如果需要,可以通过
+1
将rand(@seed+1)
其本身设置为随机值并仅由初始种子确定来避免这种情况。这样,即使系列达到相同的值,它们也会立即发散。