Генерация случайных строк с помощью T-SQL


99

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

Ответы:


41

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

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


Обратите внимание, что повторное заполнение в моем примере в основном иллюстрирует суть. На практике этого достаточно, чтобы заполнить ГСЧ один раз за сеанс, если впоследствии последовательность вызовов детерминирована.
Ремус Русану,

2
Я знаю , что это старая нить, но код возвращает ту же строку для семян 192804 и 529126
Дэвьте

1
@RemusRusanu, мне также был бы интересен ответ на комментарий Дэйви
Алекс Гордон

Семя продвигается по формуле @seed = rand(@seed+1)*2147483647. Для значения 529126 следующее значение - 1230039262, а затем следующее значение - 192804. После этого последовательность продолжается идентично. Вывод должен отличаться по первому символу, но этого не происходит из-за ошибки SUBSTRING(..., 0, ...)нечеткости : возвращает пустую строку для индекса 0, а для 529126 это «скрывает» первый сгенерированный символ. Исправление состоит в том, чтобы вычислить, @dice = rand(@seed) * len(@specials)+1чтобы индексы основывались на 1.
Ремус Русану

Эта проблема возникает из-за того, что случайные серии, как только они достигают общего значения, развиваются одинаково. В случае необходимости, его можно избежать, сделав +1в rand(@seed+1)быть сам по себе случайным и определяется только из исходного семенного материала . Таким образом, даже если ряды достигают одного и того же значения, они немедленно расходятся.
Ремус Русану

205

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

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

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


7
+1, очень просто. Совместите с RIGHT / SUBSTRING, чтобы обрезать его до необходимой длины.
Йоханнес Рудольф

2
Мне это нравится - сладко и просто. Хотя у него нет функции «предсказуемости», он отлично работает для генерации данных.
madhurtanwani

6
Вы не использовать RIGHT / подстроки усекать UUID , так как он не будет ни уникальным , ни случайным образом из - за способом генерируется UUID , !
ooxi

1
Это хорошо, но если вы используете это, убедитесь, что вы прочитали это: blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx. Примечание. GUID - это реализация UUID от Microsoft, независимо от того, как ooxi упомянул, вам нужно быть осторожным, когда вы рубите UUID.
Кларенс Лю

7
Альфа-символы в NEWID () шестнадцатеричные, поэтому вы получаете только AF, а не остальную часть алфавита. в этом смысле это ограничение.
smoore4 01

52

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

-- 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, вы можете увеличить вес определенных символов, чтобы они с большей вероятностью были выбраны.


1
+1 это хорошая процедура, я использую ее как часть хранимой процедуры.
Марчелло Миорелли 01

2
Хорошее решение. К сожалению, в udf это не работает. По какой-то причине он выдает такую ​​ошибку: «Недопустимое использование побочного оператора rand в функции».
rdans,

12
Ошибка в этой функции при вызове SUBSTRING (). Должно быть CONVERT(int, RAND() * @PoolLength) + 1(обратите внимание на добавленный +1). SUBSTRING в T-SQL начинается с индекса 1, поэтому в существующем виде эта функция иногда добавляет пустую строку (когда индекс равен 0) и никогда не добавляет последний символ из @CharPool.
Дана Картрайт,

@Dana Cartwright, не могли бы вы помочь мне с моими требованиями ... что, если мне нужна постоянная длина серийного номера с использованием указанного выше Charpool, например 10 буквенно-цифровых или 12 буквенно-цифровых серийных номеров. Как мы можем изменить хранимую процедуру. Во внешнем интерфейсе я предоставлю общее количество серийных номеров, которые будут сгенерированы, например, 100 или 200 с использованием Charpool. Минимальная длина серийного номера будет 10, а максимальная - 14 (которая также предоставляется в качестве параметра)
Prathap Gangireddy

@PrathapGangireddy, пожалуйста, задайте вопрос, комментарии одному человеку - не
Дана Картрайт

31

Используйте следующий код, чтобы вернуть короткую строку:

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

2
Возвращает только шестнадцатеричные символы: 0-9 и AF
jumxozizi

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

16

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

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

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


13
select left(NEWID(),5)

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

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

1
Хотя это решение не подходит для производственной системы, поскольку вы довольно легко получите дубликаты после нескольких тысяч или около того, оно весьма полезно для быстрого и простого способа получить случайную строку во время отладки или тестирования чего-либо.
Ian1971

Я использовал это, чтобы сгенерировать случайный 4-буквенный пароль для примерно 500 входов в систему, и это идеально подходит для этого. Да, для больших данных и других целей используйте больше символов.
Хаммад Хан

1
NEWID () не считается достаточно случайным для безопасных паролей, поэтому в зависимости от ваших требований вам нужно быть осторожным. С 5 символами вы получите коллизию примерно после 1500 записей. С 4 символами вы получите коллизию где-то от 55-800 записей в моем тесте.
Ian1971

@ Ian1971 для временного пароля, все еще в порядке. Допустим, я даю вам четырехбуквенный PIN-код для вашей карты банкомата. Такой же пин-код может быть выдан, скажем, еще одному 800-му пользователю, но шансы, что вы будете использовать его карту со своим паролем, очень маловероятны. Это в значительной степени случайное число, которое подходит для временного доступа.
Хаммад Хан

Возьмем с правой стороны, в UUID после первых восьми знаков стоит дефис, а с правой стороны после 12 - дефис. Итак, верно (NEWID (), 10)
P Satish Patro

6

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

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

Возвращает только шестнадцатеричные символы: 0-9 и AF
jumxozizi

6

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

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

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


4

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

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

Возвращает только шестнадцатеричные символы: 0-9 и AF
jumxozizi

Да, думаю, ты прав. Это не совсем буквенно-цифровой формат, поскольку вы не получаете символы> «F».
Брайан

4

Есть много хороших ответов, но пока ни один из них не позволяет настраивать пул символов и работать как значение по умолчанию для столбца. Я хотел сделать что-то вроде этого:

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

2

Я понимаю, что это старый вопрос, на который есть много хороших ответов. Однако, когда я нашел это, я также нашел более свежую статью в TechNet, написанную Саидом Хасани.

T-SQL: как сгенерировать случайные пароли

Хотя решение ориентировано на пароли, оно применимо к общему случаю. Саид обдумывает различные соображения, чтобы прийти к решению. Это очень поучительно.

Скрипт, содержащий все блоки кода из статьи, доступен отдельно в галерее TechNet , но я бы определенно начал со статьи.


1

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

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 .e.e numebrs , alpha special etc
    -- added for more felxibility in case I want to extend i.e 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

1

Иногда нам нужно много случайных вещей: любовь, доброта, отпуск и т. Д. За эти годы я собрал несколько случайных генераторов, и это от Пинала Дейва и ответа на вопрос 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

1
Спасибо за размещение коллекции удобных генераторов случайных чисел
iiminov

1

Вот один, который я придумал сегодня (потому что мне не понравился ни один из существующих ответов).

Этот генерирует временную таблицу случайных строк, основанную на newid(), но также поддерживает настраиваемый набор символов (то есть больше, чем 0-9 и AF), настраиваемую длину (до 255, ограничение жестко задано, но может быть изменено) и произвольное количество случайных записей.

Вот исходный код (надеюсь, комментарии помогут):

/**
 * First, we're going to define the random parameters for this
 * snippet. Changing these variables will alter the entire
 * outcome of this script. Try not to break everything.
 *
 * @var {int}       count    The number of random values to generate.
 * @var {int}       length   The length of each random value.
 * @var {char(62)}  charset  The characters that may appear within a random value.
 */

-- Define the parameters
declare @count int = 10
declare @length int = 60
declare @charset char(62) = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'

/**
 * We're going to define our random table to be twice the maximum
 * length (255 * 2 = 510). It's twice because we will be using
 * the newid() method, which produces hex guids. More later.
 */

-- Create the random table
declare @random table (
    value nvarchar(510)
)

/**
 * We'll use two characters from newid() to make one character in
 * the random value. Each newid() provides us 32 hex characters,
 * so we'll have to make multiple calls depending on length.
 */

-- Determine how many "newid()" calls we'll need per random value
declare @iterations int = ceiling(@length * 2 / 32.0)

/**
 * Before we start making multiple calls to "newid", we need to
 * start with an initial value. Since we know that we need at
 * least one call, we will go ahead and satisfy the count.
 */

-- Iterate up to the count
declare @i int = 0 while @i < @count begin set @i = @i + 1

    -- Insert a new set of 32 hex characters for each record, limiting to @length * 2
    insert into @random
        select substring(replace(newid(), '-', ''), 1, @length * 2)

end

-- Now fill the remaining the remaining length using a series of update clauses
set @i = 0 while @i < @iterations begin set @i = @i + 1

    -- Append to the original value, limit @length * 2
    update @random
        set value = substring(value + replace(newid(), '-', ''), 1, @length * 2)

end

/**
 * Now that we have our base random values, we can convert them
 * into the final random values. We'll do this by taking two
 * hex characters, and mapping then to one charset value.
 */

-- Convert the base random values to charset random values
set @i = 0 while @i < @length begin set @i = @i + 1

    /**
     * Explaining what's actually going on here is a bit complex. I'll
     * do my best to break it down step by step. Hopefully you'll be
     * able to follow along. If not, then wise up and come back.
     */

    -- Perform the update
    update @random
        set value =

            /**
             * Everything we're doing here is in a loop. The @i variable marks
             * what character of the final result we're assigning. We will
             * start off by taking everything we've already done first.
             */

            -- Take the part of the string up to the current index
            substring(value, 1, @i - 1) +

            /**
             * Now we're going to convert the two hex values after the index,
             * and convert them to a single charset value. We can do this
             * with a bit of math and conversions, so function away!
             */

            -- Replace the current two hex values with one charset value
            substring(@charset, convert(int, convert(varbinary(1), substring(value, @i, 2), 2)) * (len(@charset) - 1) / 255 + 1, 1) +
    --  (1) -------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^-----------------------------------------
    --  (2) ---------------------------------^^^^^^^^^^^^^^^^^^^^^^11111111111111111111111^^^^-------------------------------------
    --  (3) --------------------^^^^^^^^^^^^^2222222222222222222222222222222222222222222222222^------------------------------------
    --  (4) --------------------333333333333333333333333333333333333333333333333333333333333333---^^^^^^^^^^^^^^^^^^^^^^^^^--------
    --  (5) --------------------333333333333333333333333333333333333333333333333333333333333333^^^4444444444444444444444444--------
    --  (6) --------------------5555555555555555555555555555555555555555555555555555555555555555555555555555555555555555555^^^^----
    --  (7) ^^^^^^^^^^^^^^^^^^^^66666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666^^^^

            /**
             * (1) - Determine the two hex characters that we'll be converting (ex: 0F, AB, 3C, etc.)
             * (2) - Convert those two hex characters to a a proper hexadecimal (ex: 0x0F, 0xAB, 0x3C, etc.)
             * (3) - Convert the hexadecimals to integers (ex: 15, 171, 60)
             * (4) - Determine the conversion ratio between the length of @charset and the range of hexadecimals (255)
             * (5) - Multiply the integer from (3) with the conversion ratio from (4) to get a value between 0 and (len(@charset) - 1)
             * (6) - Add 1 to the offset from (5) to get a value between 1 and len(@charset), since strings start at 1 in SQL
             * (7) - Use the offset from (6) and grab a single character from @subset
             */

            /**
             * All that is left is to add in everything we have left to do.
             * We will eventually process the entire string, but we will
             * take things one step at a time. Round and round we go!
             */

            -- Append everything we have left to do
            substring(value, 2 + @i, len(value))

end

-- Select the results
select value
from @random

Это не хранимая процедура, но превратить ее в такую ​​не так уж и сложно. Это также не так уж и медленно (мне потребовалось ~ 0,3 секунды, чтобы сгенерировать 1000 результатов длиной 60, что больше, чем мне лично когда-либо понадобится), что было одной из моих первоначальных проблем в связи со всеми мутациями строк, которые я делаю.

Главный вывод здесь заключается в том, что я не пытаюсь создать собственный генератор случайных чисел, и мой набор символов не ограничен. Я просто использую генератор случайных чисел, который есть в SQL (я знаю, что он есть rand(), но это не очень хорошо для результатов таблицы). Надеюсь, этот подход объединяет здесь два типа ответов: от чрезмерно простого (т.е. справедливого newid()) и чрезмерно сложного (т.е. настраиваемого алгоритма случайных чисел).

Кроме того, он короткий (без комментариев) и легкий для понимания (по крайней мере, для меня), что всегда является плюсом в моей книге.

Однако этот метод не может быть засеян, поэтому каждый раз он будет действительно случайным, и вы не сможете реплицировать один и тот же набор данных с помощью каких-либо средств надежности. OP не указывал это как требование, но я знаю, что некоторые люди ищут такие вещи.

Я знаю, что опаздываю на вечеринку, но, надеюсь, кому-то это пригодится.


1

В результате будет получена строка длиной 96 символов из диапазона Base64 (верхние, нижние, числа, + и /). Добавление 3 «NEWID ()» увеличит длину на 32, без заполнения Base64 (=).

    SELECT 
        CAST(
            CONVERT(NVARCHAR(MAX),
                CONVERT(VARBINARY(8), NEWID())
                +CONVERT(VARBINARY(8), NEWID())
                +CONVERT(VARBINARY(8), NEWID())
                +CONVERT(VARBINARY(8), NEWID())
                +CONVERT(VARBINARY(8), NEWID())
                +CONVERT(VARBINARY(8), NEWID())
                +CONVERT(VARBINARY(8), NEWID())
                +CONVERT(VARBINARY(8), NEWID())
                +CONVERT(VARBINARY(8), NEWID())
            ,2) 
        AS XML).value('xs:base64Binary(xs:hexBinary(.))', 'VARCHAR(MAX)') AS StringValue

Если вы применяете это к набору, обязательно введите что-то из этого набора, чтобы NEWID () был пересчитан, иначе вы будете каждый раз получать одно и то же значение:

  SELECT 
    U.UserName
    , LEFT(PseudoRandom.StringValue, LEN(U.Pwd)) AS FauxPwd
  FROM Users U
    CROSS APPLY (
        SELECT 
            CAST(
                CONVERT(NVARCHAR(MAX),
                    CONVERT(VARBINARY(8), NEWID())
                    +CONVERT(VARBINARY(8), NEWID())
                    +CONVERT(VARBINARY(8), NEWID())
                    +CONVERT(VARBINARY(8), NEWID())
                    +CONVERT(VARBINARY(8), NEWID())
                    +CONVERT(VARBINARY(8), NEWID())
                    +CONVERT(VARBINARY(8), NEWID())
                    +CONVERT(VARBINARY(8), NEWID())
                    +CONVERT(VARBINARY(8), NEWID())
                    +CONVERT(VARBINARY(8), NEWID())
                    +CONVERT(VARBINARY(8), U.UserID)  -- Causes a recomute of all NEWID() calls
                ,2) 
            AS XML).value('xs:base64Binary(xs:hexBinary(.))', 'VARCHAR(MAX)') AS StringValue
    ) PseudoRandom

1

Для SQL Server 2016 и более поздних версий вот действительно простое и относительно эффективное выражение для генерации криптографически случайных строк заданной длины в байтах:

--Generates 36 bytes (48 characters) of base64 encoded random data
select r from OpenJson((select Crypt_Gen_Random(36) r for json path)) 
  with (r varchar(max))

Обратите внимание, что длина байта не совпадает с закодированным размером; используйте следующее из этой статьи для преобразования:

Bytes = 3 * (LengthInCharacters / 4) - Padding

0

Сначала я наткнулся на это сообщение в блоге , а затем придумал для этого следующую хранимую процедуру, которую я использую в текущем проекте (извините за странное форматирование):

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

0

Я сделал это в SQL 2000, создав таблицу, в которой были символы, которые я хотел использовать, создав представление, которое выбирает символы из этой таблицы, упорядочивая их с помощью newid (), а затем выбрав верхний 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()

Нет необходимости в представлении (на самом деле я не уверен, почему я это сделал - код написан несколько лет назад). Вы по-прежнему можете указать символы, которые хотите использовать в таблице.


0

Вот что-то, основанное на New 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)

Возвращает только шестнадцатеричные символы: 0-9 и AF
jumxozizi

0

Я думал, что поделюсь или верну сообществу ... Он основан на ASCII, и решение не идеально, но работает довольно хорошо. Наслаждайтесь, Горан Б.

/* 
-- 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 (i.e. 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

Я действительно понимаю, что мое решение - НЕ случайная генерация символов, а скорее предсказуемая обфускация строк ... :)
Горан Б.

0

Здесь используется rand с семенем, как в одном из других ответов, но нет необходимости предоставлять семя при каждом вызове. Достаточно предоставить его при первом звонке.

Это мой модифицированный код.

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

0

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

Если вам нужны более длинные строки (или вы видите =символы в результате), вам нужно добавить больше + CAST(newid() as varbinary(max))в подменю.


0

Так что мне понравились многие из приведенных выше ответов, но я искал что-то более случайное по своей природе. Мне также нужен был способ явно вызывать исключенные символы. Ниже представлено мое решение, использующее представление, которое вызывает метод CRYPT_GEN_RANDOMдля получения криптографического случайного числа. В моем примере я выбрал только случайное число размером 8 байтов. Обратите внимание, что вы можете увеличить этот размер, а также использовать параметр seed функции, если хотите. Вот ссылка на документацию: 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];

Причина создания представления заключается в том, что CRYPT_GEN_RANDOMего нельзя вызвать напрямую из функции.

Оттуда я создал скалярную функцию, которая принимает длину и строковый параметр, который может содержать строку исключенных символов с разделителями-запятыми.

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,'!,@,#,$,%,&,?');

~ Ура


0

это очень просто. используйте и наслаждайтесь.

CREATE VIEW [dbo].[vwGetNewId]
AS
SELECT        NEWID() AS Id

Creat FUNCTION [dbo].[fnGenerateRandomString](@length INT = 8)
RETURNS NVARCHAR(MAX)
AS
BEGIN

DECLARE @result CHAR(2000);

DECLARE @String VARCHAR(2000);

SET @String = 'abcdefghijklmnopqrstuvwxyz' + --lower letters
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + --upper letters
'1234567890'; --number characters

SELECT @result =
(
    SELECT TOP (@length)
           SUBSTRING(@String, 1 + number, 1) AS [text()]
    FROM master..spt_values
    WHERE number < DATALENGTH(@String)
          AND type = 'P'
    ORDER BY
(
    SELECT TOP 1 Id FROM dbo.vwGetNewId
)   --instead of using newid()
    FOR XML PATH('')
);

RETURN @result;

END;

0

Основываясь на различных полезных ответах в этой статье, я выбрал комбинацию из пары вариантов, которые мне понравились.

DECLARE @UserId BIGINT = 12345 -- a uniqueId in my system
SELECT LOWER(REPLACE(NEWID(),'-','')) + CONVERT(VARCHAR, @UserId)
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.