Générer des chaînes aléatoires avec T-SQL

96

Si vous vouliez générer une chaîne alphanumérique pseudo-aléatoire à l'aide de T-SQL, comment le feriez-vous? Comment excluriez-vous des caractères tels que les signes dollar, les tirets et les barres obliques?

Scott Lawrence
la source

Réponses:

40

Lors de la génération de données aléatoires, spécialement pour le test, il est très utile de rendre les données aléatoires, mais reproductibles. Le secret est d'utiliser des valeurs de départ explicites pour la fonction aléatoire, de sorte que lorsque le test est réexécuté avec la même valeur de départ, il produit à nouveau exactement les mêmes chaînes. Voici un exemple simplifié d'une fonction qui génère des noms d'objets de manière reproductible:

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

Lors de l'exécution des tests, l'appelant génère une graine aléatoire qu'il associe à l'exécution du test (l'enregistre dans le tableau des résultats), puis transmise la graine, semblable à ceci:

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;  

Mise à jour 17/02/2016: Voir les commentaires ci-dessous, la procédure d'origine avait un problème dans la façon dont elle avançait la graine aléatoire. J'ai mis à jour le code et j'ai également corrigé le problème mentionné ci-dessus.

Remus Rusanu
la source
Notez que le réensemencement dans mon exemple est principalement pour illustrer le point. En pratique, il suffit d'amorcer le RNG une fois par session, tant que la séquence d'appel est ensuite déterministe.
Remus Rusanu
2
Je sais que c'est vieux fil, mais le code retourne la même chaîne pour les graines 192804 et 529126
Davey
1
@RemusRusanu je serais également intéressé par une réponse au commentaire de
Davey
La graine avance avec la formule @seed = rand(@seed+1)*2147483647. Pour la valeur 529126, la valeur suivante est 1230039262 et la valeur suivante est 192804. La séquence continue de manière identique après cela. La sortie doit différer par le premier caractère, mais ce n'est pas le cas à cause d'une erreur SUBSTRING(..., 0, ...)de décalage : renvoie une chaîne vide pour l'index 0, et pour 529126, cela «masque» le premier caractère généré. Le correctif consiste à calculer @dice = rand(@seed) * len(@specials)+1pour rendre les index 1 basés.
Remus Rusanu
Ce problème vient du fait que les séries aléatoires, une fois qu'elles atteignent une valeur commune, progressent à l'identique. Si nécessaire, cela peut être évité en rendant le +1dans le rand(@seed+1)lui-même aléatoire et déterminé à partir de la graine initiale uniquement. De cette façon, même si les séries atteignent la même valeur, elles divergent immédiatement.
Remus Rusanu
202

Utiliser un guid

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

très court ...

Stefan Steinegger
la source
7
+1, très facile. Combinez-le avec RIGHT / SUBSTRING pour le tronquer à la longueur requise.
Johannes Rudolph
2
J'aime ça - doux et simple. Bien qu'il n'ait pas la fonction de «prédictibilité», il fonctionne très bien pour la génération de données.
madhurtanwani
6
N'utilisez pas RIGHT / SUBSTRING pour tronquer un UUID car il ne sera ni unique ni aléatoire en raison de la façon dont les UUID sont générés!
ooxi
1
C'est bien, mais si vous utilisez ceci, assurez-vous de lire ceci: blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx - Remarque GUID est l'implémentation de l'UUID par Microsoft, quel que soit le point ooxi mentionné, vous devez faire attention à la façon dont vous découpez l'UUID.
Clarence Liu
7
Les caractères alpha dans NEWID () sont hexadécimaux, donc vous n'obtenez que AF, pas le reste de l'alphabet. c'est limitatif dans ce sens.
smoore4
52

Similaire au premier exemple, mais avec plus de flexibilité:

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

J'ai oublié de mentionner l'une des autres fonctionnalités qui rend cela plus flexible. En répétant des blocs de caractères dans @CharPool, vous pouvez augmenter la pondération de certains caractères afin qu'ils soient plus susceptibles d'être choisis.

Chris juge
la source
1
+1 c'est une bonne routine, je l'utilise dans le cadre d'une procédure stockée.
Marcello Miorelli
2
Belle solution. Malheureusement, cela ne fonctionne pas dans un udf. Pour une raison quelconque, il donne cette erreur: "Utilisation incorrecte d'un opérateur à effet secondaire 'rand' dans une fonction".
rdans
12
Il y a un bogue dans cette fonction lors de l'appel SUBSTRING (). Cela devrait être CONVERT(int, RAND() * @PoolLength) + 1(notez le +1 ajouté). SUBSTRING dans T-SQL commence par l'index 1, donc dans sa forme actuelle, cette fonction ajoute parfois une chaîne vide (lorsque l'index est 0), et n'ajoute jamais le dernier caractère du @CharPool.
Dana Cartwright
@Dana Cartwright pouvez-vous s'il vous plaît m'aider avec mes besoins .. que faire si j'ai besoin d'une longueur constante de numéro de série en utilisant le Charpool ci-dessus comme 10 alphanumériques ou 12 alphanumériques numéro de série. Comment pouvons-nous changer la procédure stockée. Dans le front-end, je fournirai le nombre total de numéros de série à générer comme 100 ou 200 à l'aide du Charpool. La longueur minimale du numéro de série sera 10 et max 14 (qui est également fourni en tant que paramètre)
Prathap Gangireddy
@PrathapGangireddy s'il vous plaît poser une question, les commentaires à une personne ne sont pas le bon endroit pour les questions
Dana Cartwright
31

Utilisez le code suivant pour renvoyer une courte chaîne:

SELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9)
Escarcha
la source
2
Renvoie
19

Si vous exécutez SQL Server 2008 ou une version ultérieure, vous pouvez utiliser la nouvelle fonction cryptographique crypt_gen_random (), puis utiliser le codage base64 pour en faire une chaîne. Cela fonctionnera jusqu'à 8 000 caractères.

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
Kevin O
la source
16

Je ne suis pas expert en T-SQL, mais la manière la plus simple que j'ai déjà utilisée, c'est comme ça:

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

Cela génère deux caractères (AZ, en ascii 65-90).

Henrique
la source
13
select left(NEWID(),5)

Cela renverra les 5 caractères les plus à gauche de la chaîne guid

Example run
------------
11C89
9DB02
Hammad Khan
la source
1
Bien que cette solution ne soit pas bonne pour un système de production car vous obtiendrez des doublons assez facilement après plusieurs milliers de fois, elle est très utile pour un moyen rapide et facile d'obtenir une chaîne aléatoire lors du débogage ou du test de quelque chose.
Ian1971
J'ai utilisé cela pour générer un mot de passe aléatoire de 4 lettres pour environ 500 connexions et cela fonctionne parfaitement pour cela. Oui, pour le Big Data et à d'autres fins, utilisez plus de caractères.
Hammad Khan
1
NEWID () n'est pas considéré comme suffisamment aléatoire pour les mots de passe sécurisés, donc en fonction de vos besoins, vous devez faire attention. Avec 5 caractères, vous obtenez une collision après environ 1500 enregistrements. Avec 4 caractères, vous obtenez une collision de 55 à 800 enregistrements dans mon test.
Ian1971
@ Ian1971 pour le mot de passe temporaire, c'est toujours ok. Disons que je vous donne une broche de 4 lettres pour votre carte bancaire. Ce même code PIN peut également être émis pour un autre 800e utilisateur, mais les chances que vous utilisiez sa carte avec votre mot de passe sont très peu probables. C'est à peu près un nombre aléatoire qui convient pour un accès temporaire.
Hammad Khan
6

Voici un générateur alphanumérique aléatoire

print left(replace(newid(),'-',''),@length) //--@length is the length of random Num.
Krishna Thota
la source
Renvoie
5

Pour une lettre aléatoire, vous pouvez utiliser:

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

Une différence importante entre l'utilisation newid()et l' utilisation rand()est que si vous renvoyez plusieurs lignes, newid()est calculé séparément pour chaque ligne, alors qu'il rand()est calculé une fois pour l'ensemble de la requête.

Krubo
la source
4

Cela a fonctionné pour moi: je devais générer seulement trois caractères alphanumériques aléatoires pour un identifiant, mais cela pouvait fonctionner pour n'importe quelle longueur jusqu'à 15 ou plus.

declare @DesiredLength as int = 3;
select substring(replace(newID(),'-',''),cast(RAND()*(31-@DesiredLength) as int),@DesiredLength);
Brian
la source
Renvoie uniquement les caractères hexadécimaux: 0-9 & AF
Renvoie
Ouais, je suppose que vous avez raison. Ce n'est pas vraiment "alphanumérique" puisque vous n'obtenez pas de caractères> "F".
Brian
3

Il y a beaucoup de bonnes réponses, mais jusqu'à présent, aucune d'entre elles n'autorise un pool de caractères personnalisable et ne fonctionne comme valeur par défaut pour une colonne. Je voulais pouvoir faire quelque chose comme ça:

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

Alors je suis venu avec ça. Méfiez-vous du nombre codé en dur 32 si vous le modifiez.

-- 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
default.kramer
la source
2

Je me rends compte que c'est une vieille question avec beaucoup de bonnes réponses. Cependant, quand j'ai trouvé cela, j'ai également trouvé un article plus récent sur TechNet par Saeid Hasani

T-SQL: Comment générer des mots de passe aléatoires

Alors que la solution se concentre sur les mots de passe, elle s'applique au cas général. Saeid travaille à travers diverses considérations pour arriver à une solution. C'est très instructif.

Un script contenant tous les blocs de code de l'article est disponible séparément via la galerie TechNet , mais je commencerais certainement par l'article.

Karl Kieninger
la source
1

J'utilise cette procédure que j'ai développée simplement stiplatez les caractères que vous souhaitez pouvoir afficher dans les variables d'entrée, vous pouvez également définir la longueur. J'espère que cela formate bien, je suis nouveau pour le débordement de pile.

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
Base de donnéesDave
la source
1

Parfois, nous avons besoin de beaucoup de choses aléatoires: amour, gentillesse, vacances, etc. J'ai collecté quelques générateurs aléatoires au fil des ans, et ceux-ci proviennent de Pinal Dave et d'une réponse stackoverflow que j'ai trouvée une fois. Réf ci-dessous.

--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
Graeme
la source
1
Merci d'avoir publié une collection de générateurs aléatoires pratiques
iiminov
1

En voici une que j'ai trouvée aujourd'hui (parce que je n'aimais pas assez les réponses existantes).

Celui-ci génère une table temporaire de chaînes aléatoires, est basé sur newid() , mais prend également en charge un jeu de caractères personnalisé (donc plus que juste 0-9 et AF), une longueur personnalisée (jusqu'à 255, la limite est codée en dur, mais peut être changé) et un nombre personnalisé d'enregistrements aléatoires.

Voici le code source (j'espère que les commentaires aident):

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

Ce n'est pas une procédure stockée, mais il ne serait pas si difficile d'en faire une. Ce n'est pas non plus terriblement lent (il m'a fallu ~ 0,3 seconde pour générer 1000 résultats de longueur 60, ce qui est plus que ce dont j'aurai jamais besoin personnellement), ce qui était l'une de mes préoccupations initiales de toutes les mutations de cordes que je fais.

Le principal à retenir ici est que je n'essaye pas de créer mon propre générateur de nombres aléatoires et que mon jeu de caractères n'est pas limité. J'utilise simplement le générateur aléatoire de SQL (je sais qu'il y en a rand(), mais ce n'est pas génial pour les résultats de table). Espérons que cette approche marie les deux types de réponses ici, de trop simple (c'est-à-dire justenewid() ) et trop complexe (c'est-à-dire un algorithme de nombres aléatoires personnalisé).

C'est aussi court (moins les commentaires) et facile à comprendre (du moins pour moi), ce qui est toujours un plus dans mon livre.

Cependant, cette méthode ne peut pas être amorcée, elle sera donc vraiment aléatoire à chaque fois et vous ne pourrez pas répliquer le même ensemble de données avec aucun moyen de fiabilité. Le PO n'a pas mentionné cela comme une exigence, mais je sais que certaines personnes recherchent ce genre de chose.

Je sais que je suis en retard à la fête ici, mais j'espère que quelqu'un trouvera cela utile.

Boom
la source
1

Pour SQL Server 2016 et versions ultérieures, voici une expression vraiment simple et relativement efficace pour générer des chaînes cryptographiquement aléatoires d'une longueur d'octet donnée:

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

Notez que la longueur d'octet n'est pas la même que la taille codée; utilisez les éléments suivants de cet article pour convertir:

Bytes = 3 * (LengthInCharacters / 4) - Padding
N8allan
la source
0

Je suis d'abord tombé sur ce billet de blog , puis j'ai proposé la procédure stockée suivante pour cela que j'utilise sur un projet en cours (désolé pour le formatage étrange):

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
Scott Lawrence
la source
0

J'ai fait cela dans SQL 2000 en créant une table qui contenait des caractères que je voulais utiliser, en créant une vue qui sélectionne les caractères de cette table en triant par newid (), puis en sélectionnant le premier caractère de cette vue.

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

...

SELECT TOP 1 CodeChar FROM dbo.vwCodeCharRandom

Ensuite, vous pouvez simplement extraire des caractères de la vue et les concaténer si nécessaire.

EDIT: Inspiré par la réponse de Stephan ...

select top 1 RandomChar from tblRandomCharacters order by newid()

Pas besoin de vue (en fait je ne sais pas pourquoi j'ai fait ça - le code date de plusieurs années). Vous pouvez toujours spécifier les caractères que vous souhaitez utiliser dans le tableau.

Mayo
la source
0

Voici quelque chose basé sur 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)
Aamer Sattar
la source
Renvoie
0

Je pensais partager ou redonner à la communauté ... C'est basé sur ASCII, et la solution n'est pas parfaite mais ça marche plutôt bien. Amusez-vous bien, 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
Goran B.
la source
Je me rends compte que ma solution n'est PAS la génération aléatoire de caractères mais plutôt l'obscurcissement prévisible des chaînes ... :)
Goran B.
0

Cela utilise rand avec une graine comme l'une des autres réponses, mais il n'est pas nécessaire de fournir une graine à chaque appel. Le fournir au premier appel est suffisant.

Ceci est mon code modifié.

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
Bryan M
la source
0

Dans SQL Server 2012+, nous pourrions concaténer les binaires de certains UID (G), puis faire un base64 conversion sur le résultat.

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
;

Si vous avez besoin de chaînes plus longues (ou si vous voyez des =caractères dans le résultat), vous devez en ajouter d'autres + CAST(newid() as varbinary(max))dans la sous-sélection.

jumxozizi
la source
0

J'ai donc aimé beaucoup de réponses ci-dessus, mais je cherchais quelque chose d'un peu plus aléatoire par nature. Je voulais aussi un moyen d'appeler explicitement les personnages exclus. Voici ma solution utilisant une vue qui appelle le CRYPT_GEN_RANDOMpour obtenir un nombre aléatoire cryptographique. Dans mon exemple, je n'ai choisi qu'un nombre aléatoire de 8 octets. Veuillez noter que vous pouvez augmenter cette taille et également utiliser le paramètre de départ de la fonction si vous le souhaitez. Voici le lien vers la documentation: 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];

La raison de la création de la vue est qu'elle CRYPT_GEN_RANDOMne peut pas être appelée directement à partir d'une fonction.

À partir de là, j'ai créé une fonction scalaire qui accepte une longueur et un paramètre de chaîne pouvant contenir une chaîne délimitée par des virgules de caractères exclus.

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

Vous trouverez ci-dessous un exemple de la façon d'appeler la fonction.

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

~ Acclamations

Rogala
la source
0

c'est très simple. Utilisez-le et profitez-en.

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;
jbabaei
la source
0

Cela produira une chaîne de 96 caractères de longueur, à partir de la plage Base64 (supérieurs, inférieurs, nombres, + et /). Ajouter 3 "NEWID ()" augmentera la longueur de 32, sans remplissage en 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

Si vous appliquez ceci à un ensemble, assurez-vous d'introduire quelque chose de cet ensemble afin que le NEWID () soit recalculé, sinon vous obtiendrez la même valeur à chaque fois:

  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
Graham
la source
0

Sur la base de diverses réponses utiles dans cet article, j'ai atterri avec une combinaison de quelques options que j'ai aimées.

DECLARE @UserId BIGINT = 12345 -- a uniqueId in my system
SELECT LOWER(REPLACE(NEWID(),'-','')) + CONVERT(VARCHAR, @UserId)
James
la source