Si vous souhaitiez générer une chaîne alphanumérique pseudo-aléatoire à l'aide de T-SQL, comment procéderiez-vous? Comment excluez-vous des caractères tels que les signes dollar, les tirets et les barres obliques?
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 consiste à utiliser des valeurs initiales explicites pour la fonction aléatoire. Ainsi, lorsque le test est réexécuté avec la même valeur d'origine, il génère à nouveau exactement les mêmes chaînes. Voici un exemple simplifié d'une fonction qui génère des noms d'objet 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 la transmet, comme dans le cas suivant:
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 2016-02-17: Voir les commentaires ci-dessous, la procédure d'origine avait un problème dans la façon dont elle a avancé la graine aléatoire. J'ai mis à jour le code et corrigé le problème mentionné ci-dessus.
Utiliser un guide
SELECT @randomString = CONVERT(varchar(255), NEWID())
très court ...
Semblable 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.
Utilisez le code suivant pour renvoyer une chaîne courte:
SELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9)
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 pour un maximum de 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
select left(NEWID(),5)
Cela retournera les 5 caractères les plus à gauche de la chaîne guid
Example run
------------
11C89
9DB02
Je ne suis pas un expert en T-SQL, mais la façon la plus simple que j'ai déjà utilisée est la suivante:
select char((Rand()*25 + 65))+char((Rand()*25 + 65))
Cela génère deux caractères (A-Z, en ASCII 65-90).
Voici un générateur alpha numérique aléatoire
print left(replace(newid(),'-',''),@length) //--@length is the length of random Num.
Cela a fonctionné pour moi: je devais générer seulement trois caractères alphanumériques aléatoires pour un ID, mais cela pouvait fonctionner pour n'importe quelle longueur allant jusqu'à 15 ou plus.
declare @DesiredLength as int = 3;
select substring(replace(newID(),'-',''),cast(Rand()*(31-@DesiredLength) as int),@DesiredLength);
Pour une lettre aléatoire, vous pouvez utiliser:
select substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ',
(abs(checksum(newid())) % 26)+1, 1)
Une différence importante entre newid()
et Rand()
est que si vous renvoyez plusieurs lignes, newid()
est calculé séparément pour chaque ligne, alors que Rand()
est calculé une fois pour la requête entière.
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 de Saeid Hasani sur TechNet.
T-SQL: Comment générer des mots de passe aléatoires
Bien que la solution se concentre sur les mots de passe, elle s’applique au cas général. Saeid utilise diverses considérations pour trouver 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.
J'utilise cette procédure que j'ai développée simplement pour séparer les caractères que vous voulez pouvoir afficher dans les variables d'entrée, vous pouvez aussi définir la longueur .
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
Dans SQL Server 2012+, nous pourrions concaténer les fichiers binaires de certains (G) UID, puis effectuer une conversion base64 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 que vous voyez des caractères =
dans le résultat), vous devez ajouter plus de + CAST(newid() as varbinary(max))
dans la sous-sélection.
Je suis tombé sur cet article de blog d'abord, puis j'ai mis au point la procédure stockée suivante que j'utilise dans 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
Je l'ai fait dans SQL 2000 en créant une table contenant les caractères que je souhaitais utiliser, en créant une vue qui sélectionne les caractères de cette table en ordre de 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 au besoin.
EDIT: Inspiré par la réponse de Stephan ...
select top 1 RandomChar from tblRandomCharacters order by newid()
Pas besoin d'une vue (en fait, je ne suis pas sûr de savoir pourquoi j'ai fait cela - le code date de plusieurs années). Vous pouvez toujours spécifier les caractères que vous souhaitez utiliser dans le tableau.
Heres 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)
c'est très simplement, 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;
Ceci 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 faire lors du premier appel suffit.
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
Je pensais partager ou redonner à la communauté ... C'est basé sur ASCII, et la solution n'est pas parfaite, mais cela fonctionne assez bien .. Enjoy, 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
Parfois, nous avons besoin de beaucoup de choses aléatoires: amour, gentillesse, vacances, etc. __________ J'ai rassemblé quelques générateurs aléatoires au fil des années, ceux-ci sont de Pinal Dave et d'une réponse stackoverflow que j'ai trouvée une fois. Refs 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
En voici une qui m'est venue 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 supporte également un jeu de caractères personnalisé (donc plus que 0-9 & AF), longueur personnalisée (jusqu'à 255, la limite est codée en dur, mais peut être modifié) et un nombre personnalisé d’enregistrements aléatoires.
Voici le code source (j'espère que les commentaires aideront):
/**
* 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 de la transformer en une seule. Ce n'est pas non plus extrêmement lent (il m'a fallu environ 0,3 seconde pour générer 1 000 résultats de longueur 60, ce qui est plus que ce dont j'ai personnellement besoin), ce qui était l'une de mes préoccupations initiales dans toute la mutation de chaîne que je suis en train de faire.
La principale chose à retenir ici est que je n'essaie 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 que SQL a (je sais qu'il existe 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 newid()
) à trop complexe (c'est-à-dire un algorithme de nombres aléatoires personnalisés).
C'est aussi court (moins les commentaires) et facile à comprendre (du moins pour moi), ce qui est toujours un avantage dans mon livre.
Cependant, cette méthode ne peut pas être configurée, elle sera donc vraiment aléatoire à chaque fois et vous ne pourrez pas reproduire le même ensemble de données avec aucun moyen de fiabilité. Cela n’a pas été prévu par le PO, 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.
J'ai donc beaucoup aimé les réponses ci-dessus, mais je cherchais quelque chose d'un peu plus aléatoire. Je voulais aussi un moyen d'appeler explicitement les caractères exclus. Voici ma solution en utilisant une vue qui appelle le CRYPT_GEN_RANDOM
pour 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 utiliser également 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 création de la vue s'explique par le fait que CRYPT_GEN_RANDOM
ne peut pas être appelé 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 de caractères exclus délimitée par des virgules.
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 procédure à suivre pour appeler cette fonction.
SELECT [dbo].[fn_GenerateRandomString](8,'!,@,#,$,%,&,?');
~ A bientôt
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 être capable de 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 cela. Faites attention au numéro 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