T-SQL ile rastgele dizeler oluşturma


101

T-SQL kullanarak sözde rasgele bir alfasayısal dize oluşturmak isteseydiniz, bunu nasıl yapardınız? Dolar işaretleri, çizgiler ve eğik çizgiler gibi karakterleri ondan nasıl hariç tutarsınız?

Yanıtlar:


44

Rastgele veri oluştururken, özellikle test için, verileri rastgele ama tekrarlanabilir hale getirmek çok kullanışlıdır. İşin sırrı, rastgele işlev için açık tohumlar kullanmaktır, böylece test aynı tohumla tekrar çalıştırıldığında, tam olarak aynı dizeleri yeniden üretir. Tekrarlanabilir bir şekilde nesne adları üreten bir işlevin basitleştirilmiş bir örneği:

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

Testleri çalıştırırken, arayan kişi test çalıştırmasıyla ilişkilendirdiği rastgele bir çekirdek oluşturur (bunu sonuçlar tablosuna kaydeder), ardından şuna benzer şekilde çekirdek boyunca geçer:

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;  

2016-02-17 Güncellemesi: Aşağıdaki yorumlara bakın, orijinal prosedürde rastgele tohumu ilerletme biçiminde bir sorun vardı. Kodu güncelledim ve ayrıca belirtilen tek tek sorunu çözdüm.


Örneğimdeki yeniden tohumlamanın çoğunlukla konuyu aydınlatmak olduğuna dikkat edin. Uygulamada, çağrı dizisi daha sonra deterministik olduğu sürece, RNG'yi oturum başına bir kez başlatmak yeterlidir.
Remus Rusanu

2
Bu eski iplik olduğunu biliyorum, ama kod tohum 192.804 ve 529126 için aynı dizeyi verir
Davey

1
@RemusRusanu Davey'in yorumuna bir yanıtla da ilgilenirim
Alex Gordon

Tohum formülle ilerler @seed = rand(@seed+1)*2147483647. 529126 değeri için sonraki değer 1230039262 ve sonraki değer 192804'tür. Sıra bundan sonra aynı şekilde devam eder. Çıktı ilk karaktere göre farklılık göstermelidir, ancak bir defaya mahsus bir hata nedeniyle değildir: SUBSTRING(..., 0, ...)dizin 0 için boş dize döndürür ve 529126 için bu, üretilen ilk karakteri 'gizler'. Düzeltme, @dice = rand(@seed) * len(@specials)+1dizinleri 1 tabanlı yapmak için hesaplamaktır .
Remus Rusanu

Bu sorun, rastgele dizilerin ortak bir değere ulaştıklarında aynı şekilde ilerledikleri gerçeğinden kaynaklanmaktadır. Gerekirse, bu yaparak önlenebilir +1içinde rand(@seed+1)kendini rastgele ve sadece ilk tohumdan belirlenebilir. Bu şekilde, seri aynı değere ulaşsa bile, anında ayrışırlar.
Remus Rusanu

208

Bir rehber kullanma

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

çok kısa ...


7
+1, çok kolay. İhtiyaç duyulan uzunluğa kısaltmak için SAĞ / ALT STRING ile birleştirin.
Johannes Rudolph

2
Bunu beğendim - tatlı ve basit. "Öngörülebilirlik" özelliğine sahip olmasa da, veri üretimi için harika çalışıyor.
madhurtanwani

6
Do not o Uuıdlerin oluşturulan yol ne eşsiz, ne de bağlı rastgele olacağından bir UUID kesecek şekilde alt dizeyi / SAĞ kullanın!
ooxi

1
Bu iyidir, ancak bunu kullanırsanız şunu okuduğunuzdan emin olun: blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx - GUID, nokta ne olursa olsun Microsoft'un UUID uygulamasıdır. ooxi'den bahsetti, UUID'yi nasıl keseceğinize dikkat etmeniz gerekiyor.
Clarence Liu

7
NEWID () 'deki alfa karakterler onaltılıktır, bu nedenle alfabenin geri kalanını değil, yalnızca AF elde edersiniz. bu anlamda sınırlayıcıdır.
smoore4

52

İlk örneğe benzer, ancak daha fazla esneklikle:

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

Bunu daha esnek kılan diğer özelliklerden birinden bahsetmeyi unuttum. @CharPool'da karakter bloklarını tekrarlayarak, seçilme olasılıklarının daha yüksek olması için belirli karakterlerin ağırlığını artırabilirsiniz.


1
+1 bu iyi bir rutin, bunu saklı bir prosedürün parçası olarak kullanıyorum.
Marcello Miorelli

2
Güzel çözüm. Maalesef bir udf içinde çalışmıyor. Bazı nedenlerden dolayı şu hatayı verir: "Bir işlev içinde yan etki yapan 'rand' işlecinin geçersiz kullanımı".
rdans

12
SUBSTRING () çağrısında bu işlevde bir hata var. Olmalıdır CONVERT(int, RAND() * @PoolLength) + 1(eklenen + 1'e dikkat edin). T-SQL'de SUBSTRING dizin 1 ile başlar, bu nedenle bu işlev bazen boş bir dize ekler (dizin 0 olduğunda) ve hiçbir zaman @CharPool.
Dana Cartwright

@Dana Cartwright lütfen ihtiyacım konusunda bana yardımcı olur musunuz .. Yukarıdaki Charpool'u kullanarak 10 Alfasayısal veya 12 alfanümerik seri numarası gibi sabit uzunlukta seri numarasına ihtiyacım olursa. Depolanan yordamı nasıl değiştiririz? Ön uçta, Charpool kullanarak 100 veya 200 gibi üretilecek toplam seri numarası sayısını sağlayacağım. Minimum seri numarası uzunluğu 10 ve maksimum 14 olacaktır (parametre olarak da sağlanır)
Prathap Gangireddy

@PrathapGangireddy lütfen bir soru sorun, bir kişiye yapılan yorumlar sorular için doğru yer değildir
Dana Cartwright

32

Kısa bir dize döndürmek için aşağıdaki kodu kullanın:

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

2
Yalnızca onaltılık karakterleri döndürür: 0-9 & AF
jumxozizi

20

SQL Server 2008 veya üstünü çalıştırıyorsanız, yeni şifreleme işlevini crypt_gen_random () kullanabilir ve ardından onu bir dizge yapmak için base64 kodlamasını kullanabilirsiniz. Bu, 8000 karaktere kadar çalışacaktır.

declare @BinaryData varbinary(max)
    , @CharacterData varchar(max)
    , @Length int = 2048

set @BinaryData=crypt_gen_random (@Length) 

set @CharacterData=cast('' as xml).value('xs:base64Binary(sql:variable("@BinaryData"))', 'varchar(max)')

print @CharacterData

16

T-SQL konusunda uzman değilim, ancak zaten kullandığım en basit yol şudur:

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

Bu iki karakter üretir (AZ, ascii 65-90'da).


14
select left(NEWID(),5)

Bu, kılavuz dizesinin en soldaki 5 karakterini döndürür

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

1
Bu çözüm bir üretim sistemi için iyi olmasa da, birkaç bin kişi sonra kopyaları oldukça kolay bir şekilde alacağınız için, hata ayıklarken veya bir şeyi test ederken rastgele bir dizi elde etmenin hızlı ve kolay bir yolu için oldukça kullanışlıdır.
Ian1971

Bunu, yaklaşık 500 giriş için rastgele 4 harfli bir şifre oluşturmak için kullandım ve bu bunun için mükemmel çalışıyor. Evet, büyük veri ve diğer amaçlar için daha fazla karakter kullanın.
Hammad Khan

1
NEWID () güvenli parolalar için yeterince rastgele kabul edilmez, bu nedenle gereksinimlerinize bağlı olarak dikkatli olmanız gerekir. 5 karakterle yaklaşık 1500 kayıttan sonra bir çarpışma yaşarsınız. 4 karakter ile testimde 55-800 kayıt arasında herhangi bir yerde çarpışma elde edersiniz.
Ian1971

@ Ian1971 için geçici şifre, yine de sorun yok. Diyelim ki size ATM kartınız için 4 harfli pin veriyorum. Aynı pin, başka bir 800. kullanıcı için de verilebilir, ancak onun kartını şifrenizle kullanma şansınız çok düşüktür. Geçici erişim için uygun olan hemen hemen rastgele bir sayıdır.
Hammad Khan

Sağ taraftan alalım, UUID'de 1. sekiz karakterden sonra kısa çizgi var, ancak 12'den sonra sağ taraftan kısa çizgi var. Yani, doğru (NEWID (), 10)
P Satish Patro

6

İşte rastgele bir alfa sayısal oluşturucu

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

Yalnızca onaltılık karakterleri döndürür: 0-9 & AF
jumxozizi

6

Rastgele bir harf için şunları kullanabilirsiniz:

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

Kullanarak arasındaki önemli bir fark newid()karşı rand()Birden fazla satır dönerseniz, yani newid()ederken, her satır için ayrı ayrı hesaplanır rand()bütün sorgu için bir kez hesaplanır.


4

Bu benim için çalıştı: Bir kimlik için yalnızca üç rastgele alfasayısal karakter üretmem gerekiyordu, ancak 15'e kadar herhangi bir uzunlukta çalışabilirdi.

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

Yalnızca onaltılık karakterleri döndürür: 0-9 & AF
jumxozizi

Evet, sanırım haklısın. Karakterler> "F" almadığınız için gerçekten "alfasayısal" değildir.
Brian

4

Pek çok iyi cevap var, ancak şimdiye kadar hiçbiri özelleştirilebilir bir karakter havuzuna izin vermiyor ve bir sütun için varsayılan bir değer olarak çalışmıyor. Bunun gibi bir şey yapabilmek istedim:

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

Ben de bunu buldum. Değiştirirseniz, sabit kodlanmış 32 numarasına dikkat edin.

-- Converts a varbinary of length N into a varchar of length N.
-- Recommend passing in the result of CRYPT_GEN_RANDOM(N).
create function GenerateToken(@randomBytes varbinary(max))
returns varchar(max) as begin

-- Limit to 32 chars to get an even distribution (because 32 divides 256) with easy math.
declare @allowedChars char(32);
set @allowedChars = 'abcdefghijklmnopqrstuvwxyz012345';

declare @oneByte tinyint;
declare @oneChar char(1);
declare @index int;
declare @token varchar(max);

set @index = 0;
set @token = '';

while @index < datalength(@randomBytes)
begin
    -- Get next byte, use it to index into @allowedChars, and append to @token.
    -- Note: substring is 1-based.
    set @index = @index + 1;
    select @oneByte = convert(tinyint, substring(@randomBytes, @index, 1));
    select @oneChar = substring(@allowedChars, 1 + (@oneByte % 32), 1); -- 32 is the number of @allowedChars
    select @token = @token + @oneChar;
end

return @token;

end

2

Bunun pek çok güzel yanıtı olan eski bir soru olduğunun farkındayım. Ancak bunu bulduğumda Saeid Hasani tarafından TechNet hakkında daha yeni bir makale buldum

T-SQL: Rastgele Parolalar Nasıl Oluşturulur

Çözüm parolalara odaklanırken genel durum için geçerlidir. Saeid, bir çözüme ulaşmak için çeşitli hususlarda çalışır. Çok öğretici.

Makaleden tüm kod bloklarını içeren bir script, TechNet Gallery'de ayrı olarak mevcuttur , ancak kesinlikle makaleden başlayacağım.


1

Ben basitçe geliştirdiğim bu prosedürü kullanıyorum, giriş değişkenlerinde görüntülenmesini istediğiniz karakterleri belirleyin, uzunluğu da tanımlayabilirsiniz. Umarım bu formatlar iyi, yığın taşması konusunda yeniyim.

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

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

)

AS

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

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

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

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

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

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

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

END;

GO

DECLARE @RANDOMSTRING VARCHAR(8000)

EXEC GenerateARandomString @RANDOMSTRING = @RANDOMSTRING OUT

SELECT @RANDOMSTRING

1

Bazen pek çok rastgele şeye ihtiyacımız var: aşk, nezaket, tatil vb. Yıllar boyunca rastgele birkaç üretici topladım ve bunlar Pinal Dave'den ve bir keresinde bulduğum bir yığın akışı yanıtından. Aşağıdaki referanslar.

--Adapted from Pinal Dave; http://blog.sqlauthority.com/2007/04/29/sql-server-random-number-generator-script-sql-query/
SELECT 
    ABS( CAST( NEWID() AS BINARY( 6)) %1000) + 1 AS RandomInt
    , CAST( (ABS( CAST( NEWID() AS BINARY( 6)) %1000) + 1)/7.0123 AS NUMERIC( 15,4)) AS RandomNumeric
    , DATEADD( DAY, -1*(ABS( CAST( NEWID() AS BINARY( 6)) %1000) + 1), GETDATE()) AS RandomDate
    --This line from http://stackoverflow.com/questions/15038311/sql-password-generator-8-characters-upper-and-lower-and-include-a-number
    , CAST((ABS(CHECKSUM(NEWID()))%10) AS VARCHAR(1)) + CHAR(ASCII('a')+(ABS(CHECKSUM(NEWID()))%25)) + CHAR(ASCII('A')+(ABS(CHECKSUM(NEWID()))%25)) + LEFT(NEWID(),5) AS RandomChar
    , ABS(CHECKSUM(NEWID()))%50000+1 AS RandomID

1
Kullanışlı rastgele üreteçlerin koleksiyonunu yayınladığınız için teşekkür ederiz
iiminov

1

İşte bugün bulduğum soru (çünkü mevcut cevapların hiçbirini yeterince beğenmedim).

Bu, rastgele dizelerden oluşan geçici bir tablo oluşturur, buna dayanır newid(), ancak aynı zamanda özel bir karakter setini (yani sadece 0-9 ve AF'den daha fazla), özel uzunluğu (255'e kadar, sınır kodludur, ancak olabilir) değiştirildi) ve özel bir rastgele kayıt sayısı.

İşte kaynak kodu (umarım yorumlar yardımcı olur):

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

Depolanan bir prosedür değil, ama onu bir prosedüre dönüştürmek o kadar da zor olmaz. Aynı zamanda korkunç derecede yavaş da değil (60 uzunluğunda 1.000 sonuç üretmem ~ 0.3 saniye sürdü, ki bu kişisel olarak ihtiyacım olandan daha fazla), bu, yaptığım tüm dizi mutasyonlarından ilk endişelerimden biriydi.

Buradaki ana çıkarım, kendi rastgele sayı oluşturucumu yaratmaya çalışmıyorum ve karakter setimin sınırlı olmaması. Ben basitçe SQL'in sahip olduğu rastgele oluşturucuyu kullanıyorum (Biliyorum rand()ama bu tablo sonuçları için harika değil). Umarım bu yaklaşım, aşırı basit (yani sadece newid()) ve aşırı karmaşık (yani özel rastgele sayı algoritması) olmak üzere buradaki iki tür cevabı birleştirir .

Aynı zamanda kısa (eksi yorumlar) ve anlaşılması kolay (en azından benim için), bu her zaman kitabımda bir artı.

Bununla birlikte, bu yöntem tohumlanamaz, bu nedenle her seferinde gerçekten rastgele olacaktır ve aynı veri kümesini herhangi bir güvenilirlik aracıyla çoğaltamazsınız. OP bunu bir gereklilik olarak listelemedi, ancak bazı insanların bu tür şeyleri aradığını biliyorum.

Buradaki partiye geç kaldığımı biliyorum ama umarım birisi bunu faydalı bulacaktır.


1

Bu, Base64 aralığından (yukarı, aşağı, sayı, + ve /) 96 karakter uzunluğunda bir dizi oluşturacaktır. 3 "NEWID ()" eklemek, Base64 dolgusu (=) olmadan uzunluğu 32 artıracaktır.

    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

Bunu bir kümeye uyguluyorsanız, bu kümeden bir şey tanıttığınızdan emin olun, böylece NEWID () yeniden hesaplanır, aksi takdirde her seferinde aynı değeri alırsınız:

  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

1

SQL Server 2016 ve sonrası için, belirli bir bayt uzunluğunda kriptografik olarak rasgele dizeler oluşturmak için gerçekten basit ve nispeten verimli bir ifade:

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

Bayt uzunluğunun kodlanmış boyutla aynı olmadığını unutmayın; dönüştürmek için bu makaleden aşağıdakileri kullanın :

Bytes = 3 * (LengthInCharacters / 4) - Padding

0

Önce bu blog gönderisine rastladım , ardından mevcut bir projede kullandığım bunun için aşağıdaki saklı yordamı buldum (garip biçimlendirme için özür dilerim):

CREATE PROCEDURE [dbo].[SpGenerateRandomString]
@sLength tinyint = 10,
@randomString varchar(50) OUTPUT
AS
BEGIN
SET NOCOUNT ON
DECLARE @counter tinyint
DECLARE @nextChar char(1)
SET @counter = 1
SET @randomString = ”

WHILE @counter <= @sLength
BEGIN
SELECT @nextChar = CHAR(48 + CONVERT(INT, (122-48+1)*RAND()))

IF ASCII(@nextChar) not in (58,59,60,61,62,63,64,91,92,93,94,95,96)
BEGIN
SELECT @randomString = @randomString + @nextChar
SET @counter = @counter + 1
END
END
END

0

Bunu SQL 2000'de kullanmak istediğim karakterlerin bulunduğu bir tablo oluşturarak, o tablodan newid () ile sıralanan karakterleri seçen bir görünüm oluşturarak ve ardından bu görünümden ilk 1 karakteri seçerek yaptım.

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

...

SELECT TOP 1 CodeChar FROM dbo.vwCodeCharRandom

Ardından, görünümden karakterleri çekebilir ve gerektiği gibi birleştirebilirsiniz.

DÜZENLEME: Stephan'ın cevabından esinlenildi ...

select top 1 RandomChar from tblRandomCharacters order by newid()

Bir görüşe gerek yok (aslında bunu neden yaptığımı bilmiyorum - kod birkaç yıl öncesinden). Tabloda kullanmak istediğiniz karakterleri yine de belirtebilirsiniz.


0

İşte Yeni Kimliğe dayalı bir şey.

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)

Yalnızca onaltılık karakterleri döndürür: 0-9 & AF
jumxozizi

0

Paylaşacağımı ya da topluluğa geri vereceğimi düşündüm ... ASCII tabanlı ve çözüm mükemmel değil ama oldukça iyi çalışıyor. İyi eğlenceler 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

i :) ... benim çözüm rasgele karakter nesil ancak dizeleri oldukça öngörülebilir şaşırtmaca DEĞİL olduğunun farkında
Goran B 'ye

0

Bu, rand'ı diğer cevaplardan biri gibi bir tohumla kullanır, ancak her çağrıda bir tohum sağlamak gerekli değildir. İlk görüşmede bunu sağlamak yeterlidir.

Bu benim değiştirilmiş kodum.

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

create procedure usp_generateIdentifier
    @minLen int = 1
    , @maxLen int = 256
    , @seed int output
    , @string varchar(8000) output
as
begin
    set nocount on;
    declare @length int;
    declare @alpha varchar(8000)
        , @digit varchar(8000)
        , @specials varchar(8000)
        , @first varchar(8000)

    select @alpha = 'qwertyuiopasdfghjklzxcvbnm'
        , @digit = '1234567890'
        , @specials = '_@#$&'
    select @first = @alpha + '_@';

    -- Establish our rand seed and store a new seed for next time
    set  @seed = (rand(@seed)*2147483647);

    select @length = @minLen + rand() * (@maxLen-@minLen);
    --print @length

    declare @dice int;
    select @dice = rand() * len(@first);
    select @string = substring(@first, @dice, 1);

    while 0 < @length 
    begin
        select @dice = rand() * 100;
        if (@dice < 10) -- 10% special chars
        begin
            select @dice = rand() * len(@specials)+1;
            select @string = @string + substring(@specials, @dice, 1);
        end
        else if (@dice < 10+10) -- 10% digits
        begin
            select @dice = rand() * len(@digit)+1;
            select @string = @string + substring(@digit, @dice, 1);
        end
        else -- rest 80% alpha
        begin
            select @dice = rand() * len(@alpha)+1;

            select @string = @string + substring(@alpha, @dice, 1);
        end

        select @length = @length - 1;   
    end
end
go

0

Gelen SQL Server 2012+ bazı (G) eşsiz kimlikleri ikilileri bağlamak ve daha sonra bir yapabilirdi base64 sonucuna dönüşüm.

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
;

Daha uzun dizelere ihtiyacınız varsa (veya =sonuçta karakterler görüyorsanız ) + CAST(newid() as varbinary(max)), alt seçime daha fazlasını eklemeniz gerekir .


0

Bu yüzden yukarıdaki cevapların çoğunu beğendim, ancak doğası gereği biraz daha rastgele bir şey arıyordum. Ayrıca, dışlanan karakterleri açıkça belirtmenin bir yolunu da istedim. Aşağıda, CRYPT_GEN_RANDOMşifreli bir rasgele sayı almak için the ' yi çağıran bir görünüm kullanan çözümüm var . Örneğimde, yalnızca 8 bayt olan rastgele bir sayı seçtim. Lütfen bu boyutu artırabileceğinizi ve ayrıca işlevin tohum parametresini de kullanabileceğinizi unutmayın. Belgelerin bağlantısı: 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];

Görünümü yaratmanın nedeni CRYPT_GEN_RANDOM, doğrudan bir işlevden çağrılamamasıdır.

Oradan, bir uzunluğu ve virgülle sınırlandırılmış dışlanmış karakter dizisini içerebilen bir dize parametresini kabul eden bir skaler işlev oluşturdum.

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

Aşağıda, işlevin nasıl çağrılacağına dair bir örnek verilmiştir.

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

~ Şerefe


0

çok basit. kullanın ve keyfini çıkarın.

CREATE VIEW [dbo].[vwGetNewId]
AS
SELECT        NEWID() AS Id

Creat FUNCTION [dbo].[fnGenerateRandomString](@length INT = 8)
RETURNS NVARCHAR(MAX)
AS
BEGIN

DECLARE @result CHAR(2000);

DECLARE @String VARCHAR(2000);

SET @String = 'abcdefghijklmnopqrstuvwxyz' + --lower letters
'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + --upper letters
'1234567890'; --number characters

SELECT @result =
(
    SELECT TOP (@length)
           SUBSTRING(@String, 1 + number, 1) AS [text()]
    FROM master..spt_values
    WHERE number < DATALENGTH(@String)
          AND type = 'P'
    ORDER BY
(
    SELECT TOP 1 Id FROM dbo.vwGetNewId
)   --instead of using newid()
    FOR XML PATH('')
);

RETURN @result;

END;

0

Bu makaledeki çeşitli yararlı yanıtlara dayanarak, beğendiğim birkaç seçeneğin birleşimiyle indim.

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

0

Aşağıda, SQL'de 4 Veya 8 Karakter Uzun Rastgele Alfanümerik Dize Oluşturmanın yolu verilmiştir.

select LEFT(CONVERT(VARCHAR(36),NEWID()),4)+RIGHT(CONVERT(VARCHAR(36),NEWID()),4)

 SELECT RIGHT(REPLACE(CONVERT(VARCHAR(36),NEWID()),'-',''),8)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.