การสร้างสตริงแบบสุ่มด้วย T-SQL


101

หากคุณต้องการสร้างสตริงตัวอักษรและตัวเลขหลอกโดยใช้ T-SQL คุณจะทำอย่างไร? คุณจะยกเว้นอักขระเช่นเครื่องหมายดอลลาร์ขีดกลางและเครื่องหมายทับได้อย่างไร

คำตอบ:


44

เมื่อสร้างข้อมูลแบบสุ่มโดยเฉพาะสำหรับการทดสอบจะมีประโยชน์มากในการทำให้ข้อมูลเป็นแบบสุ่ม แต่สามารถทำซ้ำได้ เคล็ดลับคือการใช้เมล็ดพันธุ์ที่ชัดเจนสำหรับฟังก์ชันสุ่มดังนั้นเมื่อการทดสอบถูกเรียกใช้อีกครั้งด้วยเมล็ดพันธุ์เดียวกันก็จะสร้างสตริงเดียวกันอีกครั้ง นี่คือตัวอย่างที่เรียบง่ายของฟังก์ชันที่สร้างชื่ออ็อบเจ็กต์ในลักษณะที่ทำซ้ำได้:

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

เมื่อเรียกใช้การทดสอบผู้เรียกจะสร้างเมล็ดพันธุ์แบบสุ่มซึ่งเชื่อมโยงกับการทดสอบการทำงาน (บันทึกไว้ในตารางผลลัพธ์) จากนั้นส่งต่อไปยังเมล็ดพันธุ์ซึ่งคล้ายกับสิ่งนี้:

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: ดูความคิดเห็นต่อไปนี้ขั้นตอนเดิมมีปัญหาในวิธีการขั้นสูงเมล็ดพันธุ์แบบสุ่ม ฉันอัปเดตรหัสแล้วและยังแก้ไขปัญหาแบบ off-by-one ที่กล่าวถึง


โปรดทราบว่าการเริ่มต้นใหม่ในตัวอย่างของฉันส่วนใหญ่เป็นการเพิ่มประเด็น ในทางปฏิบัติมันเพียงพอที่จะเริ่มต้น RNG หนึ่งครั้งต่อเซสชันตราบเท่าที่ลำดับการโทรถูกกำหนดในภายหลัง
Remus Rusanu

2
ฉันรู้ว่านี่เป็นเธรดเก่า แต่รหัสส่งคืนสตริงเดียวกันสำหรับ seed 192804 และ 529126
davey

1
@RemusRusanu ฉันยังสนใจที่จะตอบกลับความคิดเห็นของ Davey
Alex Gordon

@seed = rand(@seed+1)*2147483647ความก้าวหน้าของเมล็ดพันธุ์ที่มีสูตร สำหรับค่า 529126 ค่าถัดไปคือ 1230039262 จากนั้นค่าถัดไปคือ 192804 ลำดับจะยังคงเหมือนเดิมหลังจากนี้ ผลลัพธ์ควรแตกต่างกันตามอักขระตัวแรก แต่ไม่ได้เป็นเพราะข้อผิดพลาดแบบ off-by-one: SUBSTRING(..., 0, ...)ส่งคืนสตริงว่างสำหรับดัชนี 0 และสำหรับ 529126 สิ่งนี้จะ 'ซ่อน' อักขระตัวแรกที่สร้างขึ้น การแก้ไขคือการคำนวณ@dice = rand(@seed) * len(@specials)+1เพื่อทำให้ดัชนีเป็น 1
Remus Rusanu

ปัญหานี้เกิดจากการที่ซีรีส์สุ่มเมื่อตีค่าร่วมกันแล้วจะมีการดำเนินการเหมือนกัน ถ้ามีความจำเป็นก็สามารถหลีกเลี่ยงได้โดยการทำ+1ในrand(@seed+1)เป็นตัวเองสุ่มและกำหนดจากเมล็ดเริ่มต้นเท่านั้น ด้วยวิธีนี้แม้ว่าซีรีส์จะมีค่าเท่ากัน แต่ก็แยกออกทันที
Remus Rusanu

208

การใช้คู่มือ

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

สั้นมาก ...


7
+1 ง่ายมาก รวมกับ RIGHT / SUBSTRING เพื่อตัดทอนตามความยาวที่ต้องการ
Johannes Rudolph

2
ฉันชอบสิ่งนี้ - หวานและเรียบง่าย แม้ว่าจะไม่มีคุณสมบัติ "ความสามารถในการคาดเดา" แต่ก็ใช้งานได้ดีสำหรับการสร้างข้อมูล
madhurtanwani

6
อย่าได้ใช้สิทธิ / substring ตัด UUID เพราะมันจะเป็นค่าที่ไม่ซ้ำกันมิได้สุ่มเนื่องจากวิธีการ UUIDs ถูกสร้างขึ้น!
ooxi

1
นี่เป็นสิ่งที่ดี แต่ถ้าคุณใช้สิ่งนี้โปรดอ่านสิ่งนี้: blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx - หมายเหตุ GUID คือการใช้งาน UUID ของ Microsoft ไม่ว่าประเด็นจะเป็นอย่างไร ooxi กล่าวถึงคุณต้องระวังว่าคุณจะสับ UUID อย่างไร
Clarence Liu

7
อักขระอัลฟาใน NEWID () เป็นเลขฐานสิบหกดังนั้นคุณจะได้รับ AF เท่านั้นไม่ใช่ส่วนที่เหลือของตัวอักษร มัน จำกัด ในแง่นั้น
smoore4

52

คล้ายกับตัวอย่างแรก แต่มีความยืดหยุ่นมากกว่า:

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

ฉันลืมพูดถึงคุณสมบัติอื่น ๆ ที่ทำให้มีความยืดหยุ่นมากขึ้น ด้วยการทำซ้ำบล็อกอักขระใน @CharPool คุณสามารถเพิ่มน้ำหนักให้กับอักขระบางตัวเพื่อให้มีแนวโน้มที่จะถูกเลือกมากขึ้น


1
+1 มันเป็นกิจวัตรที่ดีฉันใช้มันเป็นส่วนหนึ่งของกระบวนงานที่เก็บไว้
Marcello Miorelli

2
ทางออกที่ดี น่าเสียดายที่มันใช้งานไม่ได้ภายใน udf ด้วยเหตุผลบางประการจึงทำให้เกิดข้อผิดพลาดนี้: "การใช้ตัวดำเนินการที่มีผลข้างเคียง" Rand "ภายในฟังก์ชันไม่ถูกต้อง"
58

12
มีข้อบกพร่องในฟังก์ชันนี้ในการเรียก SUBSTRING () ควรเป็นCONVERT(int, RAND() * @PoolLength) + 1(สังเกต +1 ที่เพิ่มเข้ามา) SUBSTRING ใน T-SQL เริ่มต้นด้วยดัชนี 1 ดังนั้นในบางครั้งฟังก์ชันนี้จะเพิ่มสตริงว่าง (เมื่อดัชนีเป็น 0) และจะไม่เพิ่มอักขระสุดท้ายจากไฟล์@CharPool.
Dana Cartwright

@Dana Cartwright ได้โปรดช่วยฉันด้วยความต้องการของฉัน.. ถ้าฉันต้องการความยาวคงที่ของหมายเลขซีเรียลโดยใช้ Charpool ด้านบนเช่น 10 Alphanumeric หรือ 12 Alphanumeric Serial Number เราจะเปลี่ยนขั้นตอนการจัดเก็บได้อย่างไร ในส่วนหน้าฉันจะระบุหมายเลขซีเรียลทั้งหมดที่จะสร้างเช่น 100 หรือ 200 โดยใช้ Charpool ความยาวขั้นต่ำของหมายเลขซีเรียลจะเป็น 10 และสูงสุด 14 (ซึ่งให้มาเป็นพารามิเตอร์ด้วย)
Prathap Gangireddy

@PrathapGangireddy โปรดถามคำถามความคิดเห็นต่อบุคคลหนึ่งไม่ใช่สถานที่ที่เหมาะสมสำหรับคำถาม
Dana Cartwright

32

ใช้รหัสต่อไปนี้เพื่อส่งคืนสตริงสั้น ๆ :

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

2
แสดงเฉพาะอักขระเลขฐานสิบหก: 0-9 & AF
jumxozizi

20

หากคุณใช้ SQL Server 2008 หรือสูงกว่าคุณสามารถใช้ฟังก์ชันการเข้ารหัสใหม่ crypt_gen_random () จากนั้นใช้การเข้ารหัส base64 เพื่อทำให้เป็นสตริง ซึ่งจะใช้งานได้สูงสุด 8000 อักขระ

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 แต่วิธีที่ง่ายที่สุดที่ฉันเคยใช้มาแล้วมันเป็นเช่นนั้น

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

สิ่งนี้จะสร้างถ่านสองตัว (AZ ใน ascii 65-90)


14
select left(NEWID(),5)

สิ่งนี้จะส่งคืนอักขระ 5 ตัวที่เหลือส่วนใหญ่ของสตริง guid

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

1
แม้ว่าโซลูชันนี้จะไม่ดีสำหรับระบบการผลิตเนื่องจากคุณจะได้รับข้อมูลซ้ำอย่างง่ายดายหลังจากผ่านไปหลายพันครั้ง แต่ก็มีประโยชน์มากสำหรับวิธีที่ง่ายและรวดเร็วในการรับสตริงแบบสุ่มในขณะที่แก้จุดบกพร่องหรือทดสอบบางสิ่ง
Ian1971

ฉันใช้สิ่งนี้เพื่อสร้างรหัสผ่านแบบสุ่ม 4 ตัวอักษรสำหรับการเข้าสู่ระบบ 500 ตัวและมันก็ใช้งานได้ดี ใช่สำหรับข้อมูลขนาดใหญ่และวัตถุประสงค์อื่น ๆ ใช้อักขระมากกว่า
Hammad Khan

1
NEWID () ไม่ถือว่าสุ่มเพียงพอสำหรับรหัสผ่านที่ปลอดภัยดังนั้นคุณต้องระวังขึ้นอยู่กับความต้องการของคุณ ด้วยอักขระ 5 ตัวคุณจะได้รับการชนกันหลังจากประมาณ 1,500 รายการ ด้วยอักขระ 4 ตัวคุณจะได้รับการชนกันที่ใดก็ได้จาก 55-800 บันทึกในการทดสอบของฉัน
Ian1971

@ Ian1971 สำหรับรหัสผ่านชั่วคราวก็ยังใช้ได้ สมมติว่าฉันให้พินตัวอักษร 4 ตัวสำหรับบัตร ATM ของคุณ อาจมีการออกพินเดียวกันเพื่อบอกผู้ใช้คนที่ 800 อีกคนเช่นกัน แต่โอกาสที่คุณจะใช้บัตรของเขากับรหัสผ่านของคุณนั้นไม่น่าเป็นไปได้สูง เป็นตัวเลขสุ่มที่ใช้ได้สำหรับการเข้าถึงชั่วคราว
Hammad Khan

ลองหาจากด้านขวาใน UUID หลังจากยัติภังค์แปดตัวแรกอยู่ตรงนั้น แต่จากด้านขวาหลัง 12 จะมียัติภังค์ ใช่แล้ว (NEWID (), 10)
P Satish Patro

6

นี่คือเครื่องกำเนิดตัวเลขแบบสุ่มตัวอักษร

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

แสดงเฉพาะอักขระเลขฐานสิบหก: 0-9 & AF
jumxozizi

6

สำหรับตัวอักษรแบบสุ่มหนึ่งตัวคุณสามารถใช้:

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

ความแตกต่างที่สำคัญระหว่างการใช้newid()เทียบกับrand()คือถ้าคุณส่งคืนหลายแถวระบบnewid()จะคำนวณแยกกันสำหรับแต่ละแถวในขณะที่rand()คำนวณหนึ่งครั้งสำหรับแบบสอบถามทั้งหมด


4

สิ่งนี้ใช้ได้ผลสำหรับฉัน: ฉันต้องการสร้างอักขระตัวเลขและตัวอักษรแบบสุ่มเพียงสามตัวสำหรับ ID แต่สามารถใช้ได้กับความยาวใด ๆ ไม่เกิน 15 หรือมากกว่านั้น

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

แสดงเฉพาะอักขระเลขฐานสิบหก: 0-9 & AF
jumxozizi

ใช่ฉันเดาถูกต้อง ไม่ใช่ "ตัวเลขและตัวอักษร" ตามจริงเนื่องจากคุณไม่ได้รับอักขระ> "F"
Brian

4

มีคำตอบที่ดีมากมาย แต่จนถึงขณะนี้ไม่มีคำตอบใดที่อนุญาตให้ใช้พูลอักขระที่ปรับแต่งได้และทำงานเป็นค่าเริ่มต้นสำหรับคอลัมน์ ฉันอยากจะทำอะไรแบบนี้:

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

ผมจึงคิดสิ่งนี้ขึ้นมา ระวังหมายเลข 32 แบบฮาร์ดโค้ดหากคุณแก้ไข

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

ฉันตระหนักดีว่านี่เป็นคำถามเก่าที่มีคำตอบที่ดีมากมาย อย่างไรก็ตามเมื่อฉันพบสิ่งนี้ฉันยังพบบทความล่าสุดเกี่ยวกับ TechNet โดย Saeid Hasani

T-SQL: วิธีสร้างรหัสผ่านแบบสุ่ม

แม้ว่าโซลูชันจะมุ่งเน้นไปที่รหัสผ่าน แต่จะใช้กับกรณีทั่วไป Saeid ทำงานผ่านการพิจารณาต่างๆเพื่อหาแนวทางแก้ไข เป็นคำแนะนำมาก

สคริปต์ที่มีบล็อกโค้ดทั้งหมดจากบทความนั้นมีจำหน่ายแยกต่างหากผ่านทางTechNet Galleryแต่ฉันจะเริ่มที่บทความนี้อย่างแน่นอน


1

ฉันใช้ขั้นตอนนี้ที่ฉันพัฒนาเพียงแค่กำหนดตัวอักษรที่คุณต้องการให้สามารถแสดงในตัวแปรอินพุตคุณสามารถกำหนดความยาวได้ด้วย หวังว่ารูปแบบนี้จะดีฉันยังใหม่กับ 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

1

บางครั้งเราต้องการสิ่งสุ่มมากมายเช่นความรักความเมตตาวันหยุดพักผ่อน ฯลฯ ฉันได้รวบรวมเครื่องกำเนิดไฟฟ้าแบบสุ่มสองสามตัวในช่วงหลายปีที่ผ่านมาและสิ่งเหล่านี้มาจาก Pinal Dave และคำตอบแบบ stackoverflow ที่ฉันพบครั้งหนึ่ง อ้างอิงด้านล่าง

--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
ขอบคุณสำหรับการโพสต์คอลเลกชันของเครื่องกำเนิดไฟฟ้าแบบสุ่ม
iiminov

1

นี่คือสิ่งที่ฉันคิดขึ้นในวันนี้ (เพราะฉันไม่ชอบคำตอบที่มีอยู่มากพอ)

อันนี้สร้างตารางชั่วคราวของสตริงแบบสุ่มขึ้นอยู่กับnewid()แต่ยังรองรับชุดอักขระที่กำหนดเอง (มากกว่า 0-9 และ AF) ความยาวที่กำหนดเอง (สูงสุด 255 ขีด จำกัด คือฮาร์ดโค้ด แต่สามารถเป็นได้ เปลี่ยนแปลง) และจำนวนระเบียนแบบสุ่มที่กำหนดเอง

นี่คือซอร์สโค้ด (หวังว่าความคิดเห็นจะช่วยได้):

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

ไม่ใช่ขั้นตอนการจัดเก็บ แต่คงไม่ยากที่จะเปลี่ยนเป็นขั้นตอนเดียว นอกจากนี้ยังไม่ช้าอย่างน่ากลัว (ฉันใช้เวลาประมาณ 0.3 วินาทีในการสร้าง 1,000 ผลลัพธ์ของความยาว 60 ซึ่งมากกว่าที่ฉันต้องการเป็นการส่วนตัว) ซึ่งเป็นหนึ่งในความกังวลเบื้องต้นของฉันจากการกลายพันธุ์ของสตริงทั้งหมดที่ฉันทำ

ประเด็นหลักที่นี่คือฉันไม่ได้พยายามสร้างตัวสร้างตัวเลขสุ่มของตัวเองและชุดอักขระของฉันไม่ได้ จำกัด ฉันแค่ใช้ตัวสร้างแบบสุ่มที่ SQL มี (ฉันรู้ว่ามีrand()แต่นั่นไม่ดีสำหรับผลลัพธ์ของตาราง) หวังว่าแนวทางนี้จะผสมผสานกับคำตอบสองแบบที่นี่ตั้งแต่ง่ายเกินไป (เช่นแค่newid()) และซับซ้อนเกินไป (เช่นอัลกอริธึมตัวเลขสุ่มแบบกำหนดเอง)

นอกจากนี้ยังสั้น (ลบความคิดเห็น) และเข้าใจง่าย (อย่างน้อยสำหรับฉัน) ซึ่งเป็นข้อดีในหนังสือของฉันเสมอ

อย่างไรก็ตามวิธีนี้ไม่สามารถเริ่มต้นได้ดังนั้นจึงจะสุ่มอย่างแท้จริงทุกครั้งและคุณจะไม่สามารถจำลองข้อมูลชุดเดียวกันด้วยวิธีใดก็ได้ที่เชื่อถือได้ OP ไม่ได้ระบุว่าเป็นข้อกำหนด แต่ฉันรู้ว่าบางคนมองหาสิ่งนั้น

ฉันรู้ว่าฉันมางานปาร์ตี้ที่นี่ช้า แต่หวังว่าจะมีใครบางคนพบว่าสิ่งนี้มีประโยชน์


1

สิ่งนี้จะสร้างสตริงที่มีความยาว 96 อักขระจากช่วง Base64 (ส่วนบนสุด, ส่วนล่าง, ตัวเลข, + และ /) การเพิ่ม "NEWID ()" 3 ตัวจะทำให้ความยาวเพิ่มขึ้น 32 โดยไม่มี Base64 padding (=)

    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

หากคุณใช้สิ่งนี้กับชุดตรวจสอบให้แน่ใจว่าได้แนะนำบางสิ่งจากชุดนั้นเพื่อให้ NEWID () ถูกคำนวณใหม่มิฉะนั้นคุณจะได้รับค่าเท่ากันทุกครั้ง:

  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 และใหม่กว่านี่เป็นนิพจน์ที่เรียบง่ายและค่อนข้างมีประสิทธิภาพในการสร้างสตริงสุ่มแบบเข้ารหัสตามความยาวที่กำหนด

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

โปรดทราบว่าความยาวไบต์ไม่เหมือนกับขนาดที่เข้ารหัส ใช้ต่อไปจากนี้บทความการแปลง:

Bytes = 3 * (LengthInCharacters / 4) - Padding

0

ฉันเจอโพสต์บล็อกนี้ก่อนจากนั้นจึงคิดขั้นตอนการจัดเก็บต่อไปนี้สำหรับสิ่งนี้ที่ฉันใช้ในโครงการปัจจุบัน (ขออภัยสำหรับการจัดรูปแบบแปลก ๆ ):

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

ฉันทำสิ่งนี้ใน SQL 2000 โดยสร้างตารางที่มีอักขระที่ฉันต้องการใช้สร้างมุมมองที่เลือกอักขระจากตารางนั้นที่เรียงลำดับตาม newid () จากนั้นเลือกอักขระ 1 อันดับแรกจากมุมมองนั้น

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

...

SELECT TOP 1 CodeChar FROM dbo.vwCodeCharRandom

จากนั้นคุณสามารถดึงอักขระจากมุมมองและเชื่อมต่อกันได้ตามต้องการ

แก้ไข: แรงบันดาลใจจากการตอบสนองของ Stephan ...

select top 1 RandomChar from tblRandomCharacters order by newid()

ไม่จำเป็นต้องดู (อันที่จริงฉันไม่แน่ใจว่าทำไมฉันถึงทำอย่างนั้น - รหัสมาจากหลายปีที่ผ่านมา) คุณยังคงระบุอักขระที่ต้องการใช้ในตารางได้


0

นี่คือบางสิ่งบางอย่างตามรหัสใหม่

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)

แสดงเฉพาะอักขระเลขฐานสิบหก: 0-9 & AF
jumxozizi

0

ฉันคิดว่าฉันจะแบ่งปันหรือตอบแทนชุมชน ... มันใช้ ASCII และวิธีแก้ปัญหาก็ไม่สมบูรณ์แบบ แต่ก็ใช้ได้ดีทีเดียว สนุก 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.

0

สิ่งนี้ใช้แรนด์กับเมล็ดพันธุ์เหมือนกับคำตอบอื่น ๆ แต่ไม่จำเป็นต้องระบุเมล็ดพันธุ์ในทุกครั้งที่โทร การให้ไว้ในการโทรครั้งแรกก็เพียงพอแล้ว

นี่คือรหัสที่แก้ไขของฉัน

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

ในSQL Server 2012+เราสามารถเชื่อมไบนารีของUID (G) บางส่วนเข้าด้วยกันจากนั้นทำการแปลงbase64กับผลลัพธ์

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
;

หากคุณต้องการสตริงที่ยาวขึ้น (หรือคุณเห็น=อักขระในผลลัพธ์) คุณต้องเพิ่มเพิ่มเติม+ CAST(newid() as varbinary(max))ในตัวเลือกย่อย


0

ดังนั้นฉันจึงชอบคำตอบข้างต้นมาก แต่ฉันกำลังมองหาบางสิ่งที่สุ่มขึ้นมาเล็กน้อย ฉันยังต้องการวิธีเรียกอักขระที่ยกเว้นอย่างชัดเจน ด้านล่างนี้เป็นวิธีแก้ปัญหาของฉันโดยใช้มุมมองที่เรียกCRYPT_GEN_RANDOMให้รับหมายเลขสุ่มที่เข้ารหัสลับ ในตัวอย่างของฉันฉันเลือกเฉพาะตัวเลขสุ่มที่มีขนาด 8 ไบต์ โปรดทราบว่าคุณสามารถเพิ่มขนาดนี้และใช้พารามิเตอร์ seed ของฟังก์ชันได้หากต้องการ นี่คือลิงค์ไปยังเอกสาร: 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];

เหตุผลในการสร้างมุมมองเนื่องจากCRYPT_GEN_RANDOMไม่สามารถเรียกได้โดยตรงจากฟังก์ชัน

จากนั้นฉันสร้างฟังก์ชันสเกลาร์ที่ยอมรับความยาวและพารามิเตอร์สตริงที่สามารถมีสตริงที่คั่นด้วยจุลภาคของอักขระที่ยกเว้น

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

ด้านล่างนี้เป็นตัวอย่างวิธีการเรียกใช้ฟังก์ชัน

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

~ ไชโย


0

มันง่ายมากใช้มันและสนุก

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

จากคำตอบที่เป็นประโยชน์ต่างๆในบทความนี้ฉันได้พบกับตัวเลือกสองสามตัวที่ฉันชอบ

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

0

ด้านล่างนี้เป็นวิธีสร้างสตริงตัวอักษรและตัวเลขแบบสุ่มแบบยาว 4 หรือ 8 ตัวใน SQL

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

 SELECT RIGHT(REPLACE(CONVERT(VARCHAR(36),NEWID()),'-',''),8)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.