Zufällige Zeichenfolgen mit T-SQL generieren

95

Wenn Sie eine pseudozufällige alphanumerische Zeichenfolge mit T-SQL generieren möchten, wie würden Sie dies tun? Wie würden Sie Zeichen wie Dollarzeichen, Bindestriche und Schrägstriche davon ausschließen?

Scott Lawrence
quelle

Antworten:

40

Bei der Erzeugung zufälliger Daten, insbesondere für Tests, ist es sehr nützlich, die Daten zufällig, aber reproduzierbar zu machen. Das Geheimnis besteht darin, explizite Seeds für die Zufallsfunktion zu verwenden, sodass beim erneuten Ausführen des Tests mit demselben Seed wieder genau dieselben Zeichenfolgen erzeugt werden. Hier ist ein vereinfachtes Beispiel für eine Funktion, die Objektnamen reproduzierbar generiert:

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

Beim Ausführen der Tests generiert der Aufrufer einen zufälligen Startwert, den er dem Testlauf zuordnet (speichert ihn in der Ergebnistabelle), und leitet ihn dann ähnlich wie folgt weiter:

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;  

Update 17.02.2016: Siehe die folgenden Kommentare. Das ursprüngliche Verfahren hatte ein Problem mit der Weiterentwicklung des zufälligen Startwerts. Ich habe den Code aktualisiert und auch das erwähnte Off-by-One-Problem behoben.

Remus Rusanu
quelle
Beachten Sie, dass das erneute Säen in meinem Beispiel hauptsächlich dazu dient, den Punkt zu veranschaulichen. In der Praxis reicht es aus, das RNG einmal pro Sitzung zu setzen, solange die Anrufsequenz danach deterministisch ist.
Remus Rusanu
2
Ich weiß, dass dies ein alter Thread ist, aber der Code gibt die gleiche Zeichenfolge für Seed 192804 und 529126 zurück
Davey
1
@RemusRusanu Ich würde mich auch für eine Antwort auf Daveys Kommentar interessieren
l - '' '' '-'
Der Samen schreitet mit der Formel voran @seed = rand(@seed+1)*2147483647. Für den Wert 529126 ist der nächste Wert 1230039262 und der nächste Wert ist 192804. Die Sequenz wird danach identisch fortgesetzt. Die Ausgabe sollte sich nach dem ersten Zeichen unterscheiden, jedoch nicht aufgrund eines Off-by-One-Fehlers: SUBSTRING(..., 0, ...)Gibt eine leere Zeichenfolge für Index 0 zurück, und für 529126 wird das erste generierte Zeichen "ausgeblendet". Fix ist zu berechnen @dice = rand(@seed) * len(@specials)+1, um die Indizes 1 basierend zu machen.
Remus Rusanu
Dieses Problem ergibt sich aus der Tatsache, dass die Zufallsreihen, sobald sie einen gemeinsamen Wert erreicht haben, identisch fortschreiten. Bei Bedarf kann dies vermieden werden, indem das +1in the rand(@seed+1)be selbst zufällig gemacht und nur aus dem anfänglichen Samen bestimmt wird. Auf diese Weise weichen die Serien sofort voneinander ab, selbst wenn sie denselben Wert erreichen.
Remus Rusanu
200

Mit einem Guid

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

sehr kurze ...

Stefan Steinegger
quelle
7
+1, sehr einfach. Kombinieren Sie mit RIGHT / SUBSTRING, um es auf die erforderliche Länge zu kürzen.
Johannes Rudolph
2
Ich mag das - süß und einfach. Obwohl es nicht über die Funktion "Vorhersagbarkeit" verfügt, eignet es sich hervorragend für die Datengenerierung.
Madhurtanwani
6
Sie nicht verwenden RECHTS / SUBSTRING eine UUID gestutzt , da es weder eindeutig ist noch zufällig aufgrund der Art und Weise UUIDs erzeugt werden!
Ooxi
1
Dies ist gut, aber wenn Sie dies verwenden, lesen Sie Folgendes : blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx - Hinweis GUID ist die Implementierung von UUID durch Microsoft, unabhängig davon, wie der Punkt lautet ooxi erwähnt, müssen Sie vorsichtig sein, wie Sie die UUID zerhacken.
Clarence Liu
7
Die Alpha-Zeichen in NEWID () sind hexadezimal, sodass Sie nur AF erhalten, nicht den Rest des Alphabets. es ist in diesem Sinne einschränkend.
smoore4
51

Ähnlich wie im ersten Beispiel, jedoch mit mehr Flexibilität:

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

Ich habe vergessen, eine der anderen Funktionen zu erwähnen, die dies flexibler machen. Durch Wiederholen von Zeichenblöcken in @CharPool können Sie die Gewichtung bestimmter Zeichen erhöhen, damit sie mit größerer Wahrscheinlichkeit ausgewählt werden.

Chris Judge
quelle
1
+1 es ist eine gute Routine, ich benutze sie als Teil einer gespeicherten Prozedur.
Marcello Miorelli
2
Schöne Lösung. Leider funktioniert es nicht innerhalb eines udf. Aus irgendeinem Grund gibt es diesen Fehler: "Ungültige Verwendung eines Nebeneffektoperators 'rand' innerhalb einer Funktion".
rdans
12
Es gibt einen Fehler in dieser Funktion im Aufruf von SUBSTRING (). Es sollte sein CONVERT(int, RAND() * @PoolLength) + 1(beachten Sie die hinzugefügten +1). SUBSTRING in T-SQL beginnt mit Index 1, daher fügt diese Funktion in der jetzigen Form manchmal eine leere Zeichenfolge hinzu (wenn der Index 0 ist) und fügt niemals das letzte Zeichen aus dem hinzu @CharPool.
Dana Cartwright
@ Dana Cartwright können Sie mir bitte bei meiner Anforderung helfen. Was ist, wenn ich eine konstante Länge der Seriennummer mit der oben genannten Charpool wie 10 alphanumerische oder 12 alphanumerische Seriennummer benötige? Wie ändern wir die gespeicherte Prozedur? Im Frontend werde ich die Gesamtzahl der Seriennummern angeben, die mit dem Charpool wie 100 oder 200 generiert werden sollen. Die minimale Länge der Seriennummer beträgt 10 und die maximale Länge 14 (die auch als Parameter angegeben wird)
Prathap Gangireddy
@ PrathapGangireddy bitte stellen Sie eine Frage, Kommentare zu einer Person sind nicht der richtige Ort für Fragen
Dana Cartwright
31

Verwenden Sie den folgenden Code, um eine kurze Zeichenfolge zurückzugeben:

SELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9)
Escarcha
quelle
2
Gibt
19

Wenn Sie SQL Server 2008 oder höher ausführen, können Sie die neue kryptografische Funktion crypt_gen_random () verwenden und dann die Base64-Codierung verwenden, um daraus eine Zeichenfolge zu machen. Dies funktioniert für bis zu 8000 Zeichen.

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.
quelle
16

Ich bin kein Experte für T-SQL, aber der einfachste Weg, den ich bereits benutzt habe, ist folgender:

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

Dies erzeugt zwei Zeichen (AZ, in ASCII 65-90).

Henrique
quelle
11
select left(NEWID(),5)

Dies gibt die 5 am weitesten links stehenden Zeichen der Guid-Zeichenfolge zurück

Example run
------------
11C89
9DB02
Hammad Khan
quelle
1
Obwohl diese Lösung für ein Produktionssystem nicht gut ist, da Sie nach einigen tausend Schritten ziemlich leicht Duplikate erhalten, ist sie sehr nützlich, um schnell und einfach eine zufällige Zeichenfolge zu erhalten, während Sie etwas debuggen oder testen.
Ian1971
Ich habe dies verwendet, um ein zufälliges 4-Buchstaben-Passwort für etwa 500 Anmeldungen zu generieren, und dies funktioniert perfekt dafür. Ja, für Big Data und andere Zwecke verwenden Sie mehr Zeichen.
Hammad Khan
1
NEWID () wird für sichere Passwörter nicht als zufällig genug angesehen. Je nach Ihren Anforderungen müssen Sie also vorsichtig sein. Mit 5 Zeichen erhalten Sie nach ca. 1500 Datensätzen eine Kollision. Mit 4 Zeichen erhalten Sie in meinem Test eine Kollision zwischen 55 und 800 Datensätzen.
Ian1971
@ Ian1971 für temporäres Passwort, es ist immer noch in Ordnung. Nehmen wir an, ich gebe Ihnen eine 4-Buchstaben-PIN für Ihre Geldautomatenkarte. Dieselbe PIN könnte auch für einen weiteren 800. Benutzer vergeben werden, aber die Wahrscheinlichkeit, dass Sie seine Karte mit Ihrem Passwort verwenden, ist höchst unwahrscheinlich. Es ist so ziemlich eine Zufallszahl, die für den vorübergehenden Zugriff in Ordnung ist.
Hammad Khan
6

Hier ist ein zufälliger alphanumerischer Generator

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

Für einen zufälligen Buchstaben können Sie verwenden:

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

Ein wichtiger Unterschied zwischen der Verwendung newid()und der Verwendung rand()besteht darin, dass bei Rückgabe mehrerer Zeilen diese newid()für jede Zeile separat berechnet wird, während rand()sie für die gesamte Abfrage einmal berechnet wird.

krubo
quelle
4

Das hat bei mir funktioniert: Ich musste nur drei zufällige alphanumerische Zeichen für eine ID generieren, aber es konnte für jede Länge bis zu 15 oder so funktionieren.

declare @DesiredLength as int = 3;
select substring(replace(newID(),'-',''),cast(RAND()*(31-@DesiredLength) as int),@DesiredLength);
Brian
quelle
Gibt
Ja, ich denke du bist richtig. Es ist nicht wirklich "alphanumerisch", da Sie keine Zeichen> "F" erhalten.
Brian
3

Es gibt viele gute Antworten, aber bisher erlaubt keine einen anpassbaren Zeichenpool und funktioniert als Standardwert für eine Spalte. Ich wollte in der Lage sein, so etwas zu tun:

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

Also habe ich mir das ausgedacht. Achten Sie auf die fest codierte Nummer 32, wenn Sie sie ändern.

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

Mir ist klar, dass dies eine alte Frage mit vielen guten Antworten ist. Als ich dies fand, fand ich auch einen neueren Artikel über TechNet von Saeid Hasani

T-SQL: So generieren Sie zufällige Passwörter

Während sich die Lösung auf Passwörter konzentriert, gilt sie für den allgemeinen Fall. Saeid arbeitet verschiedene Überlegungen durch, um zu einer Lösung zu gelangen. Es ist sehr lehrreich.

Ein Skript, das alle Codeblöcke aus dem Artikel enthält, ist separat über die TechNet-Galerie verfügbar , aber ich würde definitiv mit dem Artikel beginnen.

Karl Kieninger
quelle
1

Ich verwende dieses von mir entwickelte Verfahren, indem ich einfach die Zeichen stipluiere, die Sie in den Eingabevariablen anzeigen möchten. Sie können auch die Länge definieren. Hoffe, diese Formate gut, ich bin neu im Stapelüberlauf.

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
DatabaseDave
quelle
1

Manchmal brauchen wir viele zufällige Dinge: Liebe, Freundlichkeit, Urlaub usw. Ich habe im Laufe der Jahre einige zufällige Generatoren gesammelt, die von Pinal Dave stammen und eine Stapelüberlauf-Antwort, die ich einmal gefunden habe. Refs unten.

--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
quelle
1
Vielen Dank für die Veröffentlichung einer Sammlung praktischer Zufallsgeneratoren
iiminov
1

Hier ist eine, die ich mir heute ausgedacht habe (weil mir keine der vorhandenen Antworten genug gefallen hat).

Dieser generiert eine temporäre Tabelle mit zufälligen Zeichenfolgen, basiert auf newid(), unterstützt aber auch einen benutzerdefinierten Zeichensatz (also mehr als nur 0-9 & AF) und eine benutzerdefinierte Länge (bis zu 255, Limit ist fest codiert, kann aber sein geändert) und eine benutzerdefinierte Anzahl von zufälligen Datensätzen.

Hier ist der Quellcode (hoffentlich helfen die Kommentare):

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

Es ist keine gespeicherte Prozedur, aber es wäre nicht so schwer, sie in eine zu verwandeln. Es ist auch nicht schrecklich langsam (ich habe ~ 0,3 Sekunden gebraucht, um 1.000 Ergebnisse mit einer Länge von 60 zu generieren, was mehr ist, als ich jemals persönlich brauchen werde), was eines meiner anfänglichen Probleme bei all der String-Mutation war, die ich mache.

Das Wichtigste dabei ist, dass ich nicht versuche, einen eigenen Zufallszahlengenerator zu erstellen, und mein Zeichensatz ist nicht begrenzt. Ich verwende einfach den Zufallsgenerator, den SQL hat (ich weiß rand(), dass es ihn gibt , aber das ist nicht gut für Tabellenergebnisse). Hoffentlich verbindet dieser Ansatz die beiden Arten von Antworten hier, von zu einfach (dh nur newid()) und übermäßig komplex (dh benutzerdefinierter Zufallszahlenalgorithmus).

Es ist auch kurz (abzüglich der Kommentare) und leicht zu verstehen (zumindest für mich), was in meinem Buch immer ein Plus ist.

Diese Methode kann jedoch nicht festgelegt werden, sodass sie jedes Mal wirklich zufällig ist und Sie nicht in der Lage sind, denselben Datensatz mit irgendeiner Art von Zuverlässigkeit zu replizieren. Das OP hat dies nicht als Anforderung aufgeführt, aber ich weiß, dass einige Leute nach solchen Dingen suchen.

Ich weiß, dass ich zu spät zur Party komme, aber hoffentlich findet das jemand nützlich.

Boom
quelle
0

Ich bin zuerst auf diesen Blog-Beitrag gestoßen und habe dann die folgende gespeicherte Prozedur dafür gefunden, die ich für ein aktuelles Projekt verwende (Entschuldigung für die seltsame Formatierung):

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

Ich habe dies in SQL 2000 getan, indem ich eine Tabelle mit Zeichen erstellt habe, die ich verwenden wollte, eine Ansicht erstellt habe, in der Zeichen aus dieser Tabelle ausgewählt werden, die nach newid () sortiert ist, und dann das oberste 1-Zeichen aus dieser Ansicht ausgewählt habe.

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

...

SELECT TOP 1 CodeChar FROM dbo.vwCodeCharRandom

Dann können Sie einfach Zeichen aus der Ansicht ziehen und sie nach Bedarf verketten.

EDIT: Inspiriert von Stephans Antwort ...

select top 1 RandomChar from tblRandomCharacters order by newid()

Keine Notwendigkeit für eine Ansicht (tatsächlich bin ich mir nicht sicher, warum ich das getan habe - der Code stammt aus mehreren Jahren). Sie können weiterhin die Zeichen angeben, die Sie in der Tabelle verwenden möchten.

Mayo
quelle
0

Hier ist etwas basierend auf 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
quelle
Gibt
0

Ich dachte, ich würde teilen oder der Community etwas zurückgeben ... Es basiert auf ASCII und die Lösung ist nicht perfekt, funktioniert aber recht gut. Viel Spaß, 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.
quelle
Mir ist klar, dass meine Lösung NICHT die zufällige Erzeugung von Zeichen ist, sondern eine vorhersehbare Verschleierung von Zeichenfolgen ... :)
Goran B.
0

Dies verwendet Rand mit einem Startwert wie eine der anderen Antworten, es ist jedoch nicht erforderlich, bei jedem Aufruf einen Startwert bereitzustellen. Die Bereitstellung beim ersten Anruf ist ausreichend.

Dies ist mein geänderter Code.

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.
quelle
0

In SQL Server 2012+ könnten wir die Binärdateien einiger (G) UIDs verketten und dann eine base64- Konvertierung für das Ergebnis durchführen.

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
;

Wenn Sie längere Zeichenfolgen benötigen (oder =Zeichen im Ergebnis sehen), müssen Sie + CAST(newid() as varbinary(max))in der Unterauswahl weitere hinzufügen .

jumxozizi
quelle
0

Ich mochte viele der obigen Antworten, aber ich suchte nach etwas, das etwas zufälliger war. Ich wollte auch eine Möglichkeit, ausgeschlossene Zeichen explizit aufzurufen. Unten ist meine Lösung mit einer Ansicht, die die aufruft CRYPT_GEN_RANDOM, um eine kryptografische Zufallszahl zu erhalten. In meinem Beispiel habe ich nur eine Zufallszahl ausgewählt, die 8 Byte betrug. Bitte beachten Sie, dass Sie diese Größe erhöhen und bei Bedarf auch den Startparameter der Funktion verwenden können. Hier ist der Link zur Dokumentation: 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];

Der Grund für das Erstellen der Ansicht liegt darin, dass CRYPT_GEN_RANDOMsie nicht direkt von einer Funktion aufgerufen werden kann.

Von dort aus habe ich eine Skalarfunktion erstellt, die eine Länge und einen Zeichenfolgenparameter akzeptiert, die eine durch Kommas getrennte Zeichenfolge ausgeschlossener Zeichen enthalten können.

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

Unten finden Sie ein Beispiel für den Aufruf der Funktion.

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

~ Prost

Rogala
quelle
0

Es ist sehr einfach. Verwenden Sie es und genießen Sie.

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

Dadurch wird eine Zeichenfolge mit einer Länge von 96 Zeichen aus dem Base64-Bereich (Ober-, Unter-, Zahlen, + und /) erzeugt. Durch Hinzufügen von 3 "NEWID ()" wird die Länge um 32 erhöht, ohne dass eine Base64-Auffüllung (=) erforderlich ist.

    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

Wenn Sie dies auf eine Menge anwenden, stellen Sie sicher, dass Sie etwas aus dieser Menge einführen, damit die NEWID () neu berechnet wird. Andernfalls erhalten Sie jedes Mal den gleichen Wert:

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

Für SQL Server 2016 und höher ist hier ein wirklich einfacher und relativ effizienter Ausdruck zum Generieren kryptografisch zufälliger Zeichenfolgen mit einer bestimmten Bytelänge:

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

Beachten Sie, dass die Bytelänge nicht mit der codierten Größe übereinstimmt. Verwenden Sie zum Konvertieren Folgendes aus diesem Artikel:

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

Basierend auf verschiedenen hilfreichen Antworten in diesem Artikel landete ich mit einer Kombination einiger Optionen, die mir gefallen haben.

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