Генерация случайных строк с помощью T-SQL - PullRequest
79 голосов
/ 24 августа 2009

Если вы хотите сгенерировать псевдослучайную буквенно-цифровую строку, используя T-SQL, как бы вы это сделали? Как бы вы исключили из него такие символы, как знаки доллара, тире и косые черты?

Ответы [ 23 ]

171 голосов
/ 24 августа 2009

Использование гида

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

очень короткий ...

45 голосов
/ 24 августа 2009

Аналогично первому примеру, но с большей гибкостью:

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

Я забыл упомянуть одну из других функций, которая делает это более гибким. Повторяя блоки символов в @CharPool, вы можете увеличить вес определенных символов, чтобы они с большей вероятностью были выбраны.

32 голосов
/ 24 августа 2009

При генерации случайных данных, специально для тестирования, очень полезно сделать данные случайными, но воспроизводимыми. Секрет в том, чтобы использовать явные начальные числа для случайной функции, чтобы при повторном запуске теста с тем же начальным значением снова генерировались точно такие же строки. Вот упрощенный пример функции, которая генерирует имена объектов воспроизводимым способом:

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: см. Комментарии ниже, в оригинальной процедуре возникла проблема с продвижением случайного начального числа. Я обновил код, а также устранил упомянутую проблему, не связанную с одним.

30 голосов
/ 26 апреля 2012

Используйте следующий код для возврата короткой строки:

SELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9)
16 голосов
/ 31 октября 2014

Если вы используете 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
11 голосов
/ 29 февраля 2016

Я не специалист по T-SQL, но самый простой способ, который я уже использовал, это так:

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

Это генерирует два символа (A-Z, в ascii 65-90).

10 голосов
/ 01 декабря 2014
select left(NEWID(),5)

Это вернет 5 самых левых символов строки guid

Example run
------------
11C89
9DB02
6 голосов
/ 14 августа 2013

Вот случайный альфа-числовой генератор

print left(replace(newid(),'-',''),@length) //--@length is the length of random Num.
4 голосов
/ 27 января 2014

Это сработало для меня: мне нужно было сгенерировать всего три случайных буквенно-цифровых символа для идентификатора, но он мог работать на любую длину до 15 или около того.

declare @DesiredLength as int = 3;
select substring(replace(newID(),'-',''),cast(RAND()*(31-@DesiredLength) as int),@DesiredLength);
3 голосов
/ 01 декабря 2017

Для одной случайной буквы вы можете использовать:

select substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ',
                 (abs(checksum(newid())) % 26)+1, 1)

Важным отличием использования newid() от rand() является то, что если вы возвращаете несколько строк, newid() рассчитывается отдельно для каждой строки, а rand() рассчитывается один раз для всего запроса.

...