Генерація випадкових рядків за допомогою T-SQL


96

Якщо ви хочете сформувати псевдослучайний буквено-цифровий рядок за допомогою T-SQL, як би ви це зробили? Як би ви виключили з них символи, такі як знаки долара, тире та скісні риски?

Відповіді:


40

При генерації випадкових даних, спеціально для тестування, дуже корисно зробити дані випадковими, але відтворюваними. Секрет полягає у використанні явного насіння для випадкової функції, щоб при повторному запуску тесту з тим самим насінням він знову виробляв точно такі самі рядки. Ось спрощений приклад функції, яка генерує імена об’єктів відтворюваним чином:

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;  

Оновлення 17.02.2016: Дивіться коментарі нижче, вихідна процедура мала проблему в способі просування випадкового насіння. Я оновив код, а також виправив згадану окрему проблему.


Зверніть увагу, що повторний засів у моєму прикладі здебільшого є ілюстрацією суті. На практиці цього достатньо, щоб заробити RNG один раз за сеанс, доки послідовність викликів буде детермінованою після цього.
Remus Rusanu

2
Я знаю, що це старий потік, але код повертає той самий рядок для насіння 192804 та 529126
Дейві

1
@RemusRusanu, я також зацікавлений у відповіді на коментар Дейві
l - '' '' '' ----------- '' '' '' '' '' '' '' '17

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

Ця проблема виникає внаслідок того, що випадкові ряди, досягнувши спільного значення, прогресують однаково. У разі необхідності, його можна уникнути, зробивши +1в rand(@seed+1)бути сам по собі випадковим і визначається тільки з вихідного насіннєвого матеріалу . Таким чином, навіть якщо серії мають однакове значення, вони негайно розходяться.
Ремус Русану,

202

За допомогою гіда

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

дуже короткий ...


7
+1, дуже легко. Поєднуйте з ПРАВИЛЬНИМ / ПІДТРИБОЧИМ, щоб усічити його до необхідної довжини.
Йоганнес Рудольф

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

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 це хороша процедура, я використовую її як частину збереженої процедури.
Марчелло Міореллі,

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 (що також
вказується

@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 лівих символів лідируючого рядка

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

1
Хоча це рішення не підходить для виробничої системи, оскільки дублікати ви отримуєте досить легко через декілька тисяч, це цілком корисно для швидкого та простого способу отримати випадковий рядок під час налагодження чи тестування.
Ian1971

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

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

@ Ian1971 для тимчасового пароля, це все ще нормально. Скажімо, я даю вам 4-літерний штифт для вашої картки банкомату. Цей самий штифт може бути виданий, скажімо, ще одному 800-му користувачеві, але ймовірність того, що ви будете використовувати його картку з паролем, малоймовірна. Це майже випадкове число, що нормально для тимчасового доступу.
Хаммад-хан

6

Ось випадковий алфавітно-цифровий генератор

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

Повертає лише шістнадцяткові символи: 0-9 та AF
jumxozizi

5

Для одного випадкового листа ви можете використовувати:

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".
Брайан

3

Є багато хороших відповідей, але поки що жодна з них не дозволяє настроюваний пул символів і працює як значення за замовчуванням для стовпця. Я хотів мати можливість зробити щось подібне:

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
Дякуємо за розміщення колекції зручних генераторів випадкових
випадків

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

Для 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

Тоді ви можете просто витягнути символи з подання та об’єднати їх за потреби.

EDIT: Натхненний відповіддю Стефана ...

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 байт. Зверніть увагу, ви можете збільшити цей розмір, а також використовувати параметр затравки функції, якщо хочете. Ось посилання на документацію: 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

Це дасть рядок довжиною 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

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.