Generazione di stringhe casuali con T-SQL


95

Se volessi generare una stringa alfanumerica pseudocasuale utilizzando T-SQL, come lo faresti? Come escluderesti caratteri come simboli del dollaro, trattini e barre da esso?

Risposte:


40

Quando si generano dati casuali, specialmente per test, è molto utile rendere i dati casuali, ma riproducibili. Il segreto è usare semi espliciti per la funzione casuale, in modo che quando il test viene eseguito di nuovo con lo stesso seme, produca di nuovo esattamente le stesse stringhe. Ecco un esempio semplificato di una funzione che genera i nomi degli oggetti in modo riproducibile:

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

Durante l'esecuzione dei test il chiamante genera un seme casuale che associa all'esecuzione del test (lo salva nella tabella dei risultati), quindi passato lungo il seme, in modo simile a questo:

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;  

Aggiornamento 17/02/2016: vedere i commenti qui sotto, la procedura originale aveva un problema nel modo in cui faceva avanzare il seme casuale. Ho aggiornato il codice e ho anche risolto il problema di off-by-one menzionato.


Si noti che il re-seeding nel mio esempio è principalmente per illustrare il punto. In pratica è sufficiente eseguire il seeding dell'RNG una volta per sessione, a condizione che la sequenza delle chiamate sia deterministica in seguito.
Remus Rusanu

2
So che questo è un vecchio thread, ma il codice restituisce la stessa stringa per i seed 192804 e 529126
davey

1
@RemusRusanu sarei anche interessato a una risposta al commento di davey
l --'''''--------- '' '' '' '' '' ''

Il seme avanza con la formula @seed = rand(@seed+1)*2147483647. Per il valore 529126 il valore successivo è 1230039262 e quindi il valore successivo è 192804. la sequenza continua in modo identico dopo questo. L'output dovrebbe differire per il primo carattere, ma non a causa di un errore off-by-one: SUBSTRING(..., 0, ...)restituisce una stringa vuota per l'indice 0 e per 529126 questo "nasconde" il primo carattere generato. La correzione è calcolare @dice = rand(@seed) * len(@specials)+1per rendere gli indici 1 basati.
Remus Rusanu

Questo problema nasce dal fatto che le serie casuali, una volta raggiunto un valore comune, procedono in modo identico. Se necessario, può essere evitato creando il file+1 in modo che lo rand(@seed+1)stesso sia casuale e determinato solo dal seme iniziale. In questo modo, anche se le serie raggiungono lo stesso valore, divergono immediatamente.
Remus Rusanu

202

Utilizzando un guid

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

molto corto ...


7
+1, molto facile. Combina con RIGHT / SUBSTRING per troncarlo alla lunghezza necessaria.
Johannes Rudolph

2
Mi piace: dolce e semplice. Sebbene non abbia la funzione di "prevedibilità", funziona alla grande per la generazione di dati.
madhurtanwani

6
Do Non utilizzare DESTRA / SUBSTRING per troncare un UUID dal momento che non sarà né unica né casuale dovuto al modo in cui UUID vengono generati!
ooxi

1
Questo va bene, ma se lo usi assicurati di leggere questo: blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx - Nota GUID è l'implementazione di Microsoft dell'UUID , indipendentemente dal punto è come ooxi menzionato, devi stare attento a come tagli l'UUID.
Clarence Liu

7
I caratteri alfa in NEWID () sono esadecimali, quindi ottieni solo AF, non il resto dell'alfabeto. è limitante in questo senso.
smoore4

51

Simile al primo esempio, ma con maggiore flessibilità:

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

Ho dimenticato di menzionare una delle altre caratteristiche che lo rendono più flessibile. Ripetendo blocchi di caratteri in @CharPool, puoi aumentare la ponderazione di determinati caratteri in modo che sia più probabile che vengano scelti.


1
+1 è una buona routine, lo sto usando come parte di una procedura memorizzata.
Marcello Miorelli

2
Bella soluzione. Purtroppo non funziona all'interno di un udf. Per qualche ragione dà questo errore: "Uso non valido di un operatore con effetti collaterali 'rand' all'interno di una funzione".
rdans

12
C'è un bug in questa funzione nella chiamata SUBSTRING (). Dovrebbe essere CONVERT(int, RAND() * @PoolLength) + 1(nota il +1 aggiunto). SUBSTRING in T-SQL inizia con l'indice 1, quindi così com'è questa funzione a volte aggiunge una stringa vuota (quando l'indice è 0) e non aggiunge mai l'ultimo carattere da @CharPool.
Dana Cartwright

@Dana Cartwright puoi aiutarmi con le mie esigenze .. e se avessi bisogno di una lunghezza costante del numero di serie usando il Charpool sopra come 10 alfanumerici o 12 numeri di serie alfanumerici. Come cambiamo la procedura memorizzata. Nel front-end fornirò il numero totale di numeri di serie da generare come 100 o 200 utilizzando Charpool. La lunghezza minima del numero di serie sarà 10 e massima 14 (che viene fornita anche come parametro)
Prathap Gangireddy

@PrathapGangireddy per favore fai una domanda, i commenti a una persona non sono il posto giusto per le domande
Dana Cartwright

31

Usa il codice seguente per restituire una breve stringa:

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

2
Restituisce solo caratteri esadecimali: 0-9 e AF
jumxozizi

19

Se si esegue SQL Server 2008 o versione successiva, è possibile utilizzare la nuova funzione crittografica crypt_gen_random () e quindi utilizzare la codifica base64 per renderla una stringa. Funzionerà fino a 8000 caratteri.

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

Non sono esperto in T-SQL, ma il modo più semplice che ho già usato è così:

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

Questo genera due caratteri (AZ, in ascii 65-90).


11
select left(NEWID(),5)

Ciò restituirà i 5 caratteri più a sinistra della stringa guid

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

1
Sebbene questa soluzione non sia buona per un sistema di produzione poiché otterrai duplicati abbastanza facilmente dopo diverse migliaia di volte, è abbastanza utile per un modo semplice e veloce per ottenere una stringa casuale durante il debug o il test di qualcosa.
Ian1971

L'ho usato per generare una password casuale di 4 lettere per circa 500 accessi e funziona perfettamente per questo. Sì, per i big data e altri scopi usa più caratteri.
Hammad Khan

1
NEWID () non è considerato abbastanza casuale per le password sicure, quindi a seconda delle tue esigenze devi stare attento. Con 5 caratteri si ottiene una collisione dopo circa 1500 record. Con 4 caratteri ottieni una collisione ovunque da 55-800 record nel mio test.
Ian1971

@ Ian1971 per la password temporanea, è ancora ok. Diciamo che ti do 4 lettere per il tuo bancomat. Lo stesso pin potrebbe essere assegnato anche a un altro 800 ° utente, ma le possibilità che tu possa utilizzare la sua carta con la tua password sono altamente improbabili. È praticamente un numero casuale che va bene per l'accesso temporaneo.
Hammad Khan

6

Ecco un generatore alfanumerico casuale

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

Restituisce solo caratteri esadecimali: 0-9 e AF
jumxozizi

5

Per una lettera casuale, puoi usare:

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

Una differenza importante tra l'utilizzo di newid()versus rand()è che se restituisci più righe, newid()viene calcolato separatamente per ciascuna riga, mentre rand()viene calcolato una volta per l'intera query.


4

Questo ha funzionato per me: avevo bisogno di generare solo tre caratteri alfanumerici casuali per un ID, ma poteva funzionare per qualsiasi lunghezza fino a 15 circa.

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

Restituisce solo caratteri esadecimali: 0-9 e AF
jumxozizi

Sì, immagino che tu abbia ragione. Non è veramente "alfanumerico" poiché non ottieni caratteri> "F".
Brian

3

Ci sono molte buone risposte, ma finora nessuna di esse consente un pool di caratteri personalizzabile e funziona come valore predefinito per una colonna. Volevo essere in grado di fare qualcosa del genere:

alter table MY_TABLE add MY_COLUMN char(20) not null
  default dbo.GenerateToken(crypt_gen_random(20))

Quindi ho pensato a questo. Fai attenzione al numero 32 hardcoded se lo modifichi.

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

Mi rendo conto che questa è una vecchia domanda con molte belle risposte. Tuttavia, quando ho trovato questo, ho trovato anche un articolo più recente su TechNet di Saeid Hasani

T-SQL: come generare password casuali

Sebbene la soluzione si concentri sulle password, si applica al caso generale. Saeid lavora attraverso varie considerazioni per arrivare a una soluzione. È molto istruttivo.

Uno script contenente tutti i blocchi di codice che compongono l'articolo è disponibile separatamente tramite la TechNet Gallery , ma inizierei sicuramente dall'articolo.


1

Io uso questa procedura che ho sviluppato semplicemente stabilendo i caratteri che vuoi poter visualizzare nelle variabili di input, puoi anche definire la lunghezza. Spero che questo formatta bene, sono nuovo per lo stack overflow.

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

A volte abbiamo bisogno di molte cose casuali: amore, gentilezza, vacanze, ecc. Ho raccolto alcuni generatori casuali nel corso degli anni, e questi sono di Pinal Dave e una risposta di stackoverflow che ho trovato una volta. Rif. Sotto.

--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
Grazie per aver pubblicato la raccolta di utili generatori casuali
iiminov

1

Eccone uno che ho inventato oggi (perché non mi piaceva abbastanza nessuna delle risposte esistenti).

Questo genera una tabella temporanea di stringhe casuali, è basato su newid() , ma supporta anche un set di caratteri personalizzato (quindi più che solo 0-9 e AF), lunghezza personalizzata (fino a 255, il limite è hardcoded, ma può essere modificato) e un numero personalizzato di record casuali.

Ecco il codice sorgente (si spera che i commenti aiutino):

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

Non è una procedura memorizzata, ma non sarebbe così difficile trasformarla in una. Inoltre, non è terribilmente lento (mi ci sono voluti circa 0,3 secondi per generare 1.000 risultati di lunghezza 60, che è più di quanto avrò mai bisogno personalmente), che era una delle mie preoccupazioni iniziali per tutta la mutazione delle stringhe che sto facendo.

Il punto principale qui è che non sto cercando di creare il mio generatore di numeri casuali e il mio set di caratteri non è limitato. Sto semplicemente usando il generatore casuale di SQL (so che c'è rand(), ma non è eccezionale per i risultati delle tabelle). Si spera che questo approccio coniughi i due tipi di risposte qui, da eccessivamente semplice (cioè solonewid() ) e eccessivamente complesso (cioè algoritmo di numeri casuali personalizzato).

È anche breve (meno i commenti) e facile da capire (almeno per me), il che è sempre un vantaggio nel mio libro.

Tuttavia, questo metodo non può essere inizializzato, quindi ogni volta sarà veramente casuale e non sarai in grado di replicare lo stesso set di dati con alcun mezzo di affidabilità. L'OP non lo ha elencato come requisito, ma so che alcune persone cercano quel genere di cose.

So di essere in ritardo alla festa qui, ma spero che qualcuno lo trovi utile.


0

Mi sono imbattuto prima in questo post del blog , poi ho trovato la seguente procedura memorizzata per questo che sto utilizzando su un progetto corrente (scusate per la strana formattazione):

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

L'ho fatto in SQL 2000 creando una tabella con i caratteri che volevo utilizzare, creando una vista che seleziona i caratteri da quella tabella ordinandoli per newid (), quindi selezionando il primo carattere da quella vista.

CREATE VIEW dbo.vwCodeCharRandom
AS
SELECT TOP 100 PERCENT 
    CodeChar
FROM dbo.tblCharacter
ORDER BY 
    NEWID()

...

SELECT TOP 1 CodeChar FROM dbo.vwCodeCharRandom

Quindi puoi semplicemente estrarre i caratteri dalla vista e concatenarli secondo necessità.

EDIT: Ispirato dalla risposta di Stephan ...

select top 1 RandomChar from tblRandomCharacters order by newid()

Non c'è bisogno di una vista (in effetti non sono sicuro del motivo per cui l'ho fatto - il codice è di diversi anni fa). È comunque possibile specificare i caratteri che si desidera utilizzare nella tabella.


0

Ecco qualcosa basato su 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)

Restituisce solo caratteri esadecimali: 0-9 e AF
jumxozizi

0

Ho pensato di condividere, o restituire alla comunità ... È basato su ASCII e la soluzione non è perfetta ma funziona abbastanza bene. Buon divertimento, Goran B.

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

mi rendo conto che la mia soluzione NON è la generazione di caratteri casuali ma piuttosto l'offuscamento prevedibile delle stringhe ... :)
Goran B.

0

Questo utilizza rand con un seme come una delle altre risposte, ma non è necessario fornire un seme a ogni chiamata. È sufficiente fornirlo alla prima chiamata.

Questo è il mio codice modificato.

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

In SQL Server 2012+ potremmo concatenare i file binari di alcuni (G) UID e quindi eseguire una conversione base64 sul risultato.

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
;

Se hai bisogno di stringhe più lunghe (o vedi =caratteri nel risultato) devi aggiungerne altre + CAST(newid() as varbinary(max))nella sottoselezione.


0

Quindi mi sono piaciute molte delle risposte sopra, ma stavo cercando qualcosa che fosse di natura un po 'più casuale. Volevo anche un modo per richiamare esplicitamente i personaggi esclusi. Di seguito è la mia soluzione utilizzando una vista che chiama il CRYPT_GEN_RANDOMper ottenere un numero casuale crittografico. Nel mio esempio, ho scelto solo un numero casuale di 8 byte. Nota che puoi aumentare questa dimensione e, se lo desideri, utilizzare anche il parametro seed della funzione. Ecco il link alla documentazione: 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];

Il motivo per la creazione della vista è perché CRYPT_GEN_RANDOMnon può essere chiamato direttamente da una funzione.

Da lì, ho creato una funzione scalare che accetta una lunghezza e un parametro stringa che può contenere una stringa delimitata da virgole di caratteri esclusi.

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

Di seguito è riportato un esempio di come chiamare la funzione.

SELECT [dbo].[fn_GenerateRandomString](8,'!,@,#,$,%,&,?');

~ Salute


0

è molto semplice, usalo e divertiti.

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

Questo produrrà una stringa di 96 caratteri di lunghezza, dall'intervallo Base64 (maiuscole, minuscole, numeri, + e /). L'aggiunta di 3 "NEWID ()" aumenterà la lunghezza di 32, senza riempimento 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

Se lo stai applicando a un set, assicurati di introdurre qualcosa da quel set in modo che NEWID () venga ricalcolato, altrimenti otterrai lo stesso valore ogni volta:

  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

Per SQL Server 2016 e versioni successive, ecco un'espressione davvero semplice e relativamente efficiente per generare stringhe crittograficamente casuali di una determinata lunghezza in byte:

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

Notare che la lunghezza in byte non è la stessa della dimensione codificata; utilizzare quanto segue da questo articolo per convertire:

Bytes = 3 * (LengthInCharacters / 4) - Padding

0

Sulla base di varie risposte utili in questo articolo sono arrivato con una combinazione di un paio di opzioni che mi piacevano.

DECLARE @UserId BIGINT = 12345 -- a uniqueId in my system
SELECT LOWER(REPLACE(NEWID(),'-','')) + CONVERT(VARCHAR, @UserId)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.