Generating random strings with T-SQL

前端 未结 27 1748
青春惊慌失措
青春惊慌失措 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:43

    If you are running SQL Server 2008 or greater, you could use the new cryptographic function crypt_gen_random() and then use base64 encoding to make it a string. This will work for up to 8000 characters.

    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
    
    0 讨论(0)
  • 2020-11-29 18:43

    Here's one I came up with today (because I didn't like any of the existing answers enough).

    This one generates a temp table of random strings, is based off of newid(), but also supports a custom character set (so more than just 0-9 & A-F), custom length (up to 255, limit is hard-coded, but can be changed), and a custom number of random records.

    Here's the source code (hopefully the comments help):

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

    It's not a stored procedure, but it wouldn't be that hard to turn it into one. It's also not horrendously slow (it took me ~0.3 seconds to generate 1,000 results of length 60, which is more than I'll ever personally need), which was one of my initial concerns from all of the string mutation I'm doing.

    The main takeaway here is that I'm not trying to create my own random number generator, and my character set isn't limited. I'm simply using the random generator that SQL has (I know there's rand(), but that's not great for table results). Hopefully this approach marries the two kinds of answers here, from overly simple (i.e. just newid()) and overly complex (i.e. custom random number algorithm).

    It's also short (minus the comments), and easy to understand (at least for me), which is always a plus in my book.

    However, this method cannot be seeded, so it's going to be truly random each time, and you won't be able to replicate the same set of data with any means of reliability. The OP didn't list that as a requirement, but I know that some people look for that sort of thing.

    I know I'm late to the party here, but hopefully someone will find this useful.

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

    Similar to the first example, but with more flexibility:

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

    I forgot to mention one of the other features that makes this more flexible. By repeating blocks of characters in @CharPool, you can increase the weighting on certain characters so that they are more likely to be chosen.

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

    Use the following code to return a short string:

    SELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9)
    
    0 讨论(0)
  • 2020-11-29 18:44

    This uses rand with a seed like one of the other answers, but it is not necessary to provide a seed on every call. Providing it on the first call is sufficient.

    This is my modified code.

    IF EXISTS (SELECT * FROM sys.objects WHERE type = 'P' AND object_id = OBJECT_ID(N'usp_generateIdentifier'))
    DROP PROCEDURE usp_generateIdentifier
    GO
    
    create procedure usp_generateIdentifier
        @minLen int = 1
        , @maxLen int = 256
        , @seed int output
        , @string varchar(8000) output
    as
    begin
        set nocount on;
        declare @length int;
        declare @alpha varchar(8000)
            , @digit varchar(8000)
            , @specials varchar(8000)
            , @first varchar(8000)
    
        select @alpha = 'qwertyuiopasdfghjklzxcvbnm'
            , @digit = '1234567890'
            , @specials = '_@#$&'
        select @first = @alpha + '_@';
    
        -- Establish our rand seed and store a new seed for next time
        set  @seed = (rand(@seed)*2147483647);
    
        select @length = @minLen + rand() * (@maxLen-@minLen);
        --print @length
    
        declare @dice int;
        select @dice = rand() * len(@first);
        select @string = substring(@first, @dice, 1);
    
        while 0 < @length 
        begin
            select @dice = rand() * 100;
            if (@dice < 10) -- 10% special chars
            begin
                select @dice = rand() * len(@specials)+1;
                select @string = @string + substring(@specials, @dice, 1);
            end
            else if (@dice < 10+10) -- 10% digits
            begin
                select @dice = rand() * len(@digit)+1;
                select @string = @string + substring(@digit, @dice, 1);
            end
            else -- rest 80% alpha
            begin
                select @dice = rand() * len(@alpha)+1;
    
                select @string = @string + substring(@alpha, @dice, 1);
            end
    
            select @length = @length - 1;   
        end
    end
    go
    
    0 讨论(0)
  • 2020-11-29 18:47

    In SQL Server 2012+ we could concatenate the binaries of some (G)UIDs and then do a base64 conversion on the result.

    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
    ;
    

    If you need longer strings (or you see = characters in the result) you need to add more + CAST(newid() as varbinary(max)) in the sub select.

    0 讨论(0)
提交回复
热议问题