Generating random strings with T-SQL

前端 未结 27 1747
青春惊慌失措
青春惊慌失措 2020-11-29 18:10

If you wanted to generate a pseudorandom alphanumeric string using T-SQL, how would you do it? How would you exclude characters like dollar signs, dashes, and slashes from

相关标签:
27条回答
  • 2020-11-29 18:37

    When generating random data, specially for test, it is very useful to make the data random, but reproducible. The secret is to use explicit seeds for the random function, so that when the test is run again with the same seed, it produces again exactly the same strings. Here is a simplified example of a function that generates object names in a reproducible manner:

    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
    

    When running the tests the caller generates a random seed it associates with the test run (saves it in the results table), then passed along the seed, similar to this:

    declare @seed int;
    declare @string varchar(256);
    
    select @seed = 1234; -- saved start seed
    
    exec usp_generateIdentifier 
        @seed = @seed output
        , @string = @string output;
    print @string;  
    exec usp_generateIdentifier 
        @seed = @seed output
        , @string = @string output;
    print @string;  
    exec usp_generateIdentifier 
        @seed = @seed output
        , @string = @string output;
    print @string;  
    

    Update 2016-02-17: See the comments bellow, the original procedure had an issue in the way it advanced the random seed. I updated the code, and also fixed the mentioned off-by-one issue.

    0 讨论(0)
  • 2020-11-29 18:39

    Based on various helpful responses in this article I landed with a combination of a couple options I liked.

    DECLARE @UserId BIGINT = 12345 -- a uniqueId in my system
    SELECT LOWER(REPLACE(NEWID(),'-','')) + CONVERT(VARCHAR, @UserId)
    
    0 讨论(0)
  • 2020-11-29 18:40

    I realize that this is an old question with many fine answers. However when I found this I also found a more recent article on TechNet by Saeid Hasani

    T-SQL: How to Generate Random Passwords

    While the solution focuses on passwords it applies to the general case. Saeid works through various considerations to arrive at a solution. It is very instructive.

    A script containing all the code blocks form the article is separately available via the TechNet Gallery, but I would definitely start at the article.

    0 讨论(0)
  • 2020-11-29 18:42

    Using a guid

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

    very short ...

    0 讨论(0)
  • 2020-11-29 18:42
    select left(NEWID(),5)
    

    This will return the 5 left most characters of the guid string

    Example run
    ------------
    11C89
    9DB02
    
    0 讨论(0)
  • 2020-11-29 18:42

    There are a lot of good answers but so far none of them allow a customizable character pool and work as a default value for a column. I wanted to be able to do something like this:

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

    So I came up with this. Beware of the hard-coded number 32 if you modify it.

    -- 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
    
    0 讨论(0)
提交回复
热议问题