Menghasilkan string acak dengan T-SQL

96

Jika Anda ingin membuat string alfanumerik pseudorandom menggunakan T-SQL, bagaimana Anda melakukannya? Bagaimana Anda mengecualikan karakter seperti tanda dolar, tanda hubung, dan garis miring dari itu?

Scott Lawrence
sumber

Jawaban:

40

Saat membuat data acak, khususnya untuk pengujian, sangat berguna untuk membuat data acak, tetapi dapat direproduksi. Rahasianya adalah menggunakan seed eksplisit untuk fungsi acak, sehingga ketika pengujian dijalankan lagi dengan seed yang sama, ia menghasilkan lagi string yang persis sama. Berikut adalah contoh sederhana dari sebuah fungsi yang menghasilkan nama objek dengan cara yang dapat direproduksi:

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

Saat menjalankan pengujian, pemanggil menghasilkan seed acak yang dikaitkan dengan uji coba (menyimpannya di tabel hasil), lalu meneruskan seed, mirip dengan ini:

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;  

Pembaruan 2016-02-17: Lihat komentar di bawah, prosedur asli memiliki masalah dalam cara memajukan benih acak. Saya memperbarui kode, dan juga memperbaiki masalah off-by-one yang disebutkan.

Remus Rusanu
sumber
Perhatikan bahwa penyemaian ulang dalam contoh saya sebagian besar untuk mengilustrasikan intinya. Dalam praktiknya, cukup untuk menyemai RNG sekali per sesi, selama urutan panggilan ditentukan sesudahnya.
Remus Rusanu
2
Saya tahu ini utas lama, tetapi kodenya mengembalikan string yang sama untuk seed 192804 dan 529126
davey
1
@RemusRusanu saya juga akan tertarik untuk menanggapi komentar davey
l --''''''--------- '' '' '' '' '' ''
Benih maju dengan rumus @seed = rand(@seed+1)*2147483647. Untuk nilai 529126 nilai berikutnya adalah 1230039262 kemudian nilai berikutnya adalah 192804. urutan berlanjut sama setelah ini. Keluarannya harus berbeda dengan karakter pertama, tetapi bukan karena kesalahan off-by-one: SUBSTRING(..., 0, ...)mengembalikan string kosong untuk indeks 0, dan untuk 529126 ini 'menyembunyikan' karakter pertama yang dihasilkan. Perbaiki adalah menghitung @dice = rand(@seed) * len(@specials)+1untuk membuat indeks 1 berbasis.
Remus Rusanu
Masalah ini muncul dari fakta bahwa rangkaian acak, setelah mencapai nilai yang sama, kemajuannya sama. Jika diperlukan, dapat dihindari dengan membuat +1dalam rand(@seed+1)menjadi dirinya sendiri acak dan ditentukan dari benih awal saja. Dengan cara ini, bahkan jika rangkaian mencapai nilai yang sama, keduanya segera menyimpang.
Remus Rusanu
202

Menggunakan panduan

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

sangat singkat ...

Stefan Steinegger
sumber
7
+1, sangat mudah. Gabungkan dengan RIGHT / SUBSTRING untuk memotongnya ke panjang yang dibutuhkan.
Johannes Rudolph
2
Saya suka ini - manis dan sederhana. Meskipun tidak memiliki fitur "predisitabilitas", berfungsi sangat baik untuk pembuatan data.
madhurtanwani
6
Jangan tidak menggunakan KANAN / substring memotong sebuah UUID karena akan tidak unik atau random karena cara UUIDs yang dihasilkan!
ooxi
1
Ini bagus, tetapi jika Anda menggunakan ini pastikan Anda membaca ini: blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx - Catatan GUID adalah implementasi UUID dari Microsoft, apa pun intinya adalah sebagai ooxi menyebutkan, Anda harus berhati-hati dalam memotong UUID.
Clarence Liu
7
Karakter alfa di NEWID () adalah heksadesimal, jadi Anda hanya mendapatkan AF, bukan alfabet lainnya. itu membatasi dalam pengertian itu.
smoore4
52

Mirip dengan contoh pertama, tetapi dengan lebih banyak fleksibilitas:

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

Saya lupa menyebutkan salah satu fitur lain yang membuatnya lebih fleksibel. Dengan mengulang blok karakter di @CharPool, Anda dapat meningkatkan pembobotan pada karakter tertentu sehingga lebih mungkin untuk dipilih.

Chris Judge
sumber
1
+1 ini rutinitas yang baik, saya menggunakannya sebagai bagian dari prosedur tersimpan.
Marcello Miorelli
2
Solusi bagus. Sayangnya itu tidak berfungsi dalam udf. Untuk beberapa alasan, ini memberikan kesalahan ini: "Penggunaan yang tidak valid dari operator 'rand' yang berpengaruh samping dalam suatu fungsi".
rdans
12
Ada bug dalam fungsi ini dalam panggilan SUBSTRING (). Seharusnya CONVERT(int, RAND() * @PoolLength) + 1(perhatikan +1 yang ditambahkan). SUBSTRING di T-SQL dimulai dengan indeks 1, sehingga fungsi ini terkadang menambahkan string kosong (jika indeksnya 0), dan tidak pernah menambahkan karakter terakhir dari @CharPool.
Dana Cartwright
@Dana Cartwright dapatkah Anda membantu saya dengan kebutuhan saya .. bagaimana jika saya membutuhkan panjang nomor seri konstan menggunakan Charpool di atas seperti 10 nomor seri alfanumerik atau 12 nomor seri alfanumerik. Bagaimana kita mengubah prosedur yang tersimpan. Di bagian depan saya akan memberikan jumlah total nomor seri yang akan dihasilkan seperti 100 atau 200 menggunakan Charpool. Panjang minimal nomor seri adalah 10 dan maksimal 14 (yang juga disediakan sebagai parameter)
Prathap Gangireddy
@PrathapGangireddy tolong ajukan pertanyaan, komentar untuk satu orang bukanlah tempat yang tepat untuk pertanyaan
Dana Cartwright
31

Gunakan kode berikut untuk mengembalikan string pendek:

SELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9)
Escarcha
sumber
2
Hanya mengembalikan karakter heksadesimal: 0-9 & AF
jumxozizi
19

Jika Anda menjalankan SQL Server 2008 atau yang lebih baru, Anda dapat menggunakan fungsi kriptografi baru crypt_gen_random () dan kemudian menggunakan pengkodean base64 untuk menjadikannya sebuah string. Ini akan bekerja hingga 8000 karakter.

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

Saya tidak ahli dalam T-SQL, tetapi cara paling sederhana yang pernah saya gunakan adalah seperti itu:

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

Ini menghasilkan dua karakter (AZ, di ascii 65-90).

Henrique
sumber
13
select left(NEWID(),5)

Ini akan mengembalikan 5 karakter paling kiri dari string guid

Example run
------------
11C89
9DB02
Hammad Khan
sumber
1
Meskipun solusi ini tidak baik untuk sistem produksi karena Anda akan mendapatkan duplikat dengan mudah setelah beberapa ribu atau lebih, ini cukup berguna untuk cara cepat dan mudah mendapatkan string acak saat melakukan debug atau menguji sesuatu.
Ian1971
Saya menggunakan ini untuk menghasilkan kata sandi 4 huruf acak untuk sekitar 500 login dan ini berfungsi sempurna untuk itu. Ya untuk data besar dan tujuan lain, gunakan lebih banyak karakter.
Hammad Khan
1
NEWID () tidak dianggap cukup acak untuk kata sandi yang aman, jadi tergantung pada kebutuhan Anda, Anda perlu berhati-hati. Dengan 5 karakter Anda mendapatkan tabrakan setelah sekitar 1500 rekaman. Dengan 4 karakter Anda mendapatkan tabrakan di mana saja dari 55-800 catatan dalam pengujian saya.
Ian1971
@ Ian1971 untuk kata sandi sementara, masih ok. Katakanlah saya memberikan pin 4 huruf untuk Anda kartu ATM. Pin yang sama itu mungkin dikeluarkan untuk mengatakan pengguna ke-800 lainnya juga, tetapi kemungkinan Anda akan menggunakan kartunya dengan kata sandi Anda sangat tidak mungkin. Ini adalah nomor acak yang cukup baik untuk akses sementara.
Hammad Khan
6

Berikut adalah generator alfa numerik acak

print left(replace(newid(),'-',''),@length) //--@length is the length of random Num.
Krishna Thota
sumber
Hanya mengembalikan karakter heksadesimal: 0-9 & AF
jumxozizi
5

Untuk satu huruf acak, Anda dapat menggunakan:

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

Perbedaan penting antara menggunakan newid()versus rand()adalah jika Anda mengembalikan beberapa baris, newid()dihitung secara terpisah untuk setiap baris, sementara rand()dihitung sekali untuk seluruh kueri.

krubo
sumber
4

Ini berhasil untuk saya: Saya hanya perlu membuat tiga karakter alfanumerik acak untuk sebuah ID, tetapi bisa bekerja untuk panjang apa pun hingga 15 atau lebih.

declare @DesiredLength as int = 3;
select substring(replace(newID(),'-',''),cast(RAND()*(31-@DesiredLength) as int),@DesiredLength);
Brian
sumber
Hanya mengembalikan karakter heksadesimal: 0-9 & AF
jumxozizi
Ya, saya rasa Anda benar. Ini tidak benar-benar "alfanumerik" karena Anda tidak mendapatkan karakter> "F".
Brian
3

Ada banyak jawaban bagus tetapi sejauh ini tidak ada yang mengizinkan kumpulan karakter yang dapat disesuaikan dan berfungsi sebagai nilai default untuk kolom. Saya ingin bisa melakukan sesuatu seperti ini:

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

Jadi saya datang dengan ini. Waspadalah terhadap kode keras nomor 32 jika Anda memodifikasinya.

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

Saya menyadari bahwa ini adalah pertanyaan lama dengan banyak jawaban bagus. Namun ketika saya menemukan ini, saya juga menemukan artikel yang lebih baru di TechNet oleh Saeid Hasani

T-SQL: Cara Menghasilkan Kata Sandi Acak

Sementara solusinya berfokus pada kata sandi, itu berlaku untuk kasus umum. Saeid bekerja melalui berbagai pertimbangan untuk mendapatkan solusi. Ini sangat instruktif.

Skrip yang berisi semua blok kode dari artikel tersedia secara terpisah melalui Galeri TechNet , tetapi saya pasti akan mulai dari artikel tersebut.

Karl Kieninger
sumber
1

Saya menggunakan prosedur ini yang saya kembangkan hanya dengan menetapkan karakter yang ingin Anda tampilkan di variabel input, Anda juga dapat menentukan panjangnya. Semoga format ini baik, saya baru mengenal stack overflow.

IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND object_id = OBJECT_ID(N'GenerateARandomString'))
DROP PROCEDURE GenerateARandomString
GO

CREATE PROCEDURE GenerateARandomString
(
     @DESIREDLENGTH         INTEGER = 100,                  
     @NUMBERS               VARCHAR(50) 
        = '0123456789',     
     @ALPHABET              VARCHAR(100) 
        ='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
     @SPECIALS              VARCHAR(50) 
        = '_=+-$£%^&*()"!@~#:', 
     @RANDOMSTRING          VARCHAR(8000)   OUT 

)

AS

BEGIN
    -- Author David Riley
    -- Version 1.0 
    -- You could alter to one big string .e.e numebrs , alpha special etc
    -- added for more felxibility in case I want to extend i.e put logic  in for 3 numbers, 2 pecials 3 numbers etc
    -- for now just randomly pick one of them

    DECLARE @SWAP                   VARCHAR(8000);      -- Will be used as a tempoary buffer 
    DECLARE @SELECTOR               INTEGER = 0;

    DECLARE @CURRENTLENGHT          INTEGER = 0;
    WHILE @CURRENTLENGHT < @DESIREDLENGTH
    BEGIN

        -- Do we want a number, special character or Alphabet Randonly decide?
        SET @SELECTOR  = CAST(ABS(CHECKSUM(NEWID())) % 3 AS INTEGER);   -- Always three 1 number , 2 alphaBET , 3 special;
        IF @SELECTOR = 0
        BEGIN
            SET @SELECTOR = 3
        END;

        -- SET SWAP VARIABLE AS DESIRED
        SELECT @SWAP = CASE WHEN @SELECTOR = 1 THEN @NUMBERS WHEN @SELECTOR = 2 THEN @ALPHABET ELSE @SPECIALS END;

        -- MAKE THE SELECTION
        SET @SELECTOR  = CAST(ABS(CHECKSUM(NEWID())) % LEN(@SWAP) AS INTEGER);
        IF @SELECTOR = 0
        BEGIN
            SET @SELECTOR = LEN(@SWAP)
        END;

        SET @RANDOMSTRING = ISNULL(@RANDOMSTRING,'') + SUBSTRING(@SWAP,@SELECTOR,1);
        SET @CURRENTLENGHT = LEN(@RANDOMSTRING);
    END;

END;

GO

DECLARE @RANDOMSTRING VARCHAR(8000)

EXEC GenerateARandomString @RANDOMSTRING = @RANDOMSTRING OUT

SELECT @RANDOMSTRING
DatabaseDave
sumber
1

Terkadang kita membutuhkan banyak hal acak: cinta, kebaikan, liburan, dll. Saya telah mengumpulkan beberapa generator acak selama bertahun-tahun, dan ini berasal dari Pinal Dave dan jawaban stackoverflow yang saya temukan sekali. Referensi di bawah.

--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
sumber
1
Terima kasih telah memposting koleksi generator acak yang berguna
iiminov
1

Inilah yang saya dapatkan hari ini (karena saya tidak cukup menyukai jawaban yang ada).

Yang ini menghasilkan tabel temp string acak, berdasarkan newid(), tetapi juga mendukung set karakter kustom (jadi lebih dari sekadar 0-9 & AF), panjang kustom (hingga 255, batas hard-coded, tetapi dapat diubah), dan sejumlah custom record acak.

Berikut kode sumbernya (semoga komentarnya membantu):

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

Ini bukan prosedur tersimpan, tetapi tidak akan sulit untuk mengubahnya menjadi satu. Ini juga tidak terlalu lambat (saya membutuhkan ~ 0,3 detik untuk menghasilkan 1.000 hasil dengan panjang 60, yang lebih dari yang saya perlukan secara pribadi), yang merupakan salah satu perhatian awal saya dari semua mutasi string yang saya lakukan.

Kesimpulan utama di sini adalah saya tidak mencoba membuat generator nomor acak saya sendiri, dan rangkaian karakter saya tidak terbatas. Saya hanya menggunakan generator acak yang dimiliki SQL (saya tahu ada rand(), tapi itu tidak bagus untuk hasil tabel). Mudah-mudahan pendekatan ini mengawinkan dua jenis jawaban di sini, dari yang terlalu sederhana (yaitu hanyanewid() ) dan terlalu kompleks (yaitu algoritma bilangan acak khusus).

Ini juga singkat (minus komentar), dan mudah dimengerti (setidaknya bagi saya), yang selalu menjadi nilai plus di buku saya.

Namun, metode ini tidak dapat diunggulkan, jadi metode ini akan benar-benar acak setiap kali, dan Anda tidak akan dapat mereplikasi kumpulan data yang sama dengan cara apa pun yang dapat diandalkan. OP tidak mencantumkannya sebagai persyaratan, tetapi saya tahu bahwa beberapa orang mencari hal semacam itu.

Saya tahu saya terlambat ke pesta di sini, tapi mudah-mudahan seseorang akan menganggap ini berguna.

Ledakan
sumber
1

Untuk SQL Server 2016 dan yang lebih baru, berikut adalah ekspresi yang sangat sederhana dan relatif efisien untuk menghasilkan string acak secara kriptografis dengan panjang byte tertentu:

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

Perhatikan bahwa panjang byte tidak sama dengan ukuran yang dikodekan; gunakan yang berikut dari artikel ini untuk mengonversi:

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

Saya menemukan posting blog ini terlebih dahulu, kemudian muncul dengan prosedur tersimpan berikut untuk ini yang saya gunakan pada proyek saat ini (maaf untuk pemformatan yang aneh):

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

Saya melakukan ini di SQL 2000 dengan membuat tabel yang memiliki karakter yang ingin saya gunakan, membuat tampilan yang memilih karakter dari tabel yang diurutkan berdasarkan newid (), lalu memilih 1 karakter teratas dari tampilan tersebut.

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

...

SELECT TOP 1 CodeChar FROM dbo.vwCodeCharRandom

Kemudian Anda cukup menarik karakter dari tampilan dan menggabungkannya sesuai kebutuhan.

EDIT: Terinspirasi oleh tanggapan Stephan ...

select top 1 RandomChar from tblRandomCharacters order by newid()

Tidak perlu melihat (sebenarnya saya tidak yakin mengapa saya melakukan itu - kode itu dari beberapa tahun yang lalu). Anda masih dapat menentukan karakter yang ingin Anda gunakan di tabel.

Mayo
sumber
0

Inilah sesuatu berdasarkan Id Baru.

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
sumber
Hanya mengembalikan karakter heksadesimal: 0-9 & AF
jumxozizi
0

Saya pikir saya akan berbagi, atau memberikan kembali kepada komunitas ... Ini berbasis ASCII, dan solusinya tidak sempurna tetapi bekerja dengan cukup baik. Selamat menikmati, 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.
sumber
saya menyadari bahwa solusi saya BUKAN pembuatan char acak melainkan kebingungan string yang dapat diprediksi ... :)
Goran B.
0

Ini menggunakan rand dengan benih seperti salah satu jawaban lainnya, tetapi tidak perlu memberikan benih pada setiap panggilan. Memberikannya pada panggilan pertama sudah cukup.

Ini adalah kode saya yang dimodifikasi.

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

Di SQL Server 2012+ kami dapat menggabungkan biner dari beberapa (G) UID dan kemudian melakukan konversi base64 pada hasilnya.

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
;

Jika Anda membutuhkan string yang lebih panjang (atau Anda melihat =karakter di hasil) Anda perlu menambahkan lebih banyak + CAST(newid() as varbinary(max))di sub pilih.

jumxozizi
sumber
0

Jadi saya menyukai banyak jawaban di atas, tetapi saya mencari sesuatu yang sifatnya sedikit lebih acak. Saya juga menginginkan cara untuk secara eksplisit memanggil karakter yang dikecualikan. Di bawah ini adalah solusi saya menggunakan tampilan yang memanggil CRYPT_GEN_RANDOMuntuk mendapatkan nomor acak kriptografi. Dalam contoh saya, saya hanya memilih nomor acak yaitu 8 byte. Harap dicatat, Anda dapat meningkatkan ukuran ini dan juga menggunakan parameter seed dari fungsi tersebut jika Anda mau. Berikut ini tautan ke dokumentasinya: 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];

Alasan untuk membuat tampilan tersebut adalah karena CRYPT_GEN_RANDOM tidak bisa dipanggil langsung dari suatu fungsi.

Dari sana, saya membuat fungsi skalar yang menerima panjang dan parameter string yang dapat berisi string yang dipisahkan koma dari karakter yang dikecualikan.

CREATE FUNCTION [dbo].[fn_GenerateRandomString]
( 
    @length INT,
    @excludedCharacters VARCHAR(200) --Comma delimited string of excluded characters
)
RETURNS VARCHAR(Max)
BEGIN
    DECLARE @returnValue VARCHAR(Max) = ''
        , @asciiValue INT
        , @currentCharacter CHAR;

    --Optional concept, you can add default excluded characters
    SET @excludedCharacters = CONCAT(@excludedCharacters,',^,*,(,),-,_,=,+,[,{,],},\,|,;,:,'',",<,.,>,/,`,~');

    --Table of excluded characters
    DECLARE @excludedCharactersTable table([asciiValue] INT);

    --Insert comma
    INSERT INTO @excludedCharactersTable SELECT 44;

    --Stores the ascii value of the excluded characters in the table
    INSERT INTO @excludedCharactersTable
    SELECT ASCII(TRIM(value))
    FROM STRING_SPLIT(@excludedCharacters, ',')
    WHERE LEN(TRIM(value)) = 1;

    --Keep looping until the return string is filled
    WHILE(LEN(@returnValue) < @length)
    BEGIN
        --Get a truly random integer values from 33-126
        SET @asciiValue = (SELECT TOP 1 (ABS(CONVERT(INT, [value])) % 94) + 33 FROM [dbo].[VW_CRYPT_GEN_RANDOM_8]);

        --If the random integer value is not in the excluded characters table then append to the return string
        IF(NOT EXISTS(SELECT * 
                        FROM @excludedCharactersTable 
                        WHERE [asciiValue] = @asciiValue))
        BEGIN
            SET @returnValue = @returnValue + CHAR(@asciiValue);
        END
    END

    RETURN(@returnValue);
END

Di bawah ini adalah contoh bagaimana memanggil fungsi tersebut.

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

~ Cheers

Rogala
sumber
0

itu sangat sederhana. gunakan dan nikmati.

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

Ini akan menghasilkan string sepanjang 96 karakter, dari rentang Base64 (bagian atas, bawah, angka, + dan /). Menambahkan 3 "NEWID ()" akan menambah panjang 32, tanpa padding 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

Jika Anda menerapkan ini ke satu set, pastikan untuk memperkenalkan sesuatu dari set itu sehingga NEWID () dihitung ulang, jika tidak, Anda akan mendapatkan nilai yang sama setiap kali:

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

Berdasarkan berbagai tanggapan bermanfaat dalam artikel ini, saya mendapatkan kombinasi dari beberapa opsi yang saya suka.

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