【问题标题】:Generating random strings with T-SQL使用 T-SQL 生成随机字符串
【发布时间】:2010-11-22 09:12:09
【问题描述】:

如果你想使用 T-SQL 生成一个伪随机字母数字字符串,你会怎么做?您将如何从中排除美元符号、破折号和斜杠等字符?

【问题讨论】:

    标签: sql tsql random


    【解决方案1】:

    使用向导

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

    很短...

    【讨论】:

    • +1,非常简单。结合 RIGHT/SUBSTRING 将其截断为所需的长度。
    • 我喜欢这个——甜美而简单。虽然它没有“可预测性”功能,但非常适合数据生成。
    • 不要不要使用 RIGHT/SUBSTRING 截断 UUID,因为 UUID 的生成方式既不唯一也不随机!
    • 这很好,但是如果你使用它,请确保你阅读了这个:blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx - 注意 GUID 是微软的 UUID 实现,不管 ooxi 提到的点,你需要小心你如何砍掉 UUID。
    • NEWID() 中的 alpha 字符是十六进制的,所以你只能得到 A-F,而不是字母表的其余部分。从这个意义上说,它是有限的。
    【解决方案2】:

    与第一个示例类似,但更灵活:

    -- 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, 1)
        SELECT @LoopCount = @LoopCount + 1
    END
    

    我忘了提到其他使这更灵活的功能之一。通过在@CharPool 中重复字符块,您可以增加某些字符的权重,以便它们更有可能被选中。

    【讨论】:

    • +1 这是一个很好的例程,我将它用作存储过程的一部分。
    • 不错的解决方案。不幸的是,它在 udf 中不起作用。由于某种原因,它给出了这个错误:“在函数中无效使用副作用运算符'rand'”。
    • 在 SUBSTRING() 调用中此函数存在错误。它应该是CONVERT(int, RAND() * @PoolLength) + 1(注意添加的+1)。 T-SQL 中的 SUBSTRING 从索引 1 开始,因此这个函数有时会添加一个空字符串(当索引为 0 时),并且从不添加 @CharPool 中的最后一个字符。
    • @PrathapGangireddy 请提问,一个人的 cmets 不是提问的正确地方
    • 是的,我在这里发布了我的问题stackoverflow.com/questions/58854667/…
    【解决方案3】:

    在生成随机数据时,特别是为了测试,使数据随机化是非常有用的,但可以重现。秘诀是对随机函数使用显式种子,这样当使用相同的种子再次运行测试时,它会再次产生完全相同的字符串。下面是一个以可重现方式生成对象名称的函数的简化示例:

    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 更新:请参阅下面的 cmets,原始程序在推进随机种子的方式上存在问题。我更新了代码,还修复了上面提到的问题。

    【讨论】:

    • 请注意,我的示例中的重新播种主要是为了说明这一点。在实践中,只要之后的调用序列是确定性的,它就足以为每个会话播种一次 RNG。
    • 我知道这是旧线程,但代码为种子 192804 和 529126 返回相同的字符串
    • @RemusRusanu 我也有兴趣回复戴维的评论
    • 种子以公式@seed = rand(@seed+1)*2147483647前进。对于值 529126,下一个值是 1230039262,然后下一个值是 192804。此后序列以相同方式继续。输出应该因第一个字符而异,但这不是因为一个错误:SUBSTRING(..., 0, ...) 返回索引 0 的空字符串,对于 529126,此“隐藏”生成的第一个字符。修复是计算 @dice = rand(@seed) * len(@specials)+1 以使索引基于 1。
    • 这个问题源于这样一个事实,即随机序列,一旦它们达到一个共同的值,它们的进展是相同的。如果需要,可以通过使rand(@seed+1) 中的+1 本身随机并仅从初始种子确定来避免这种情况。这样,即使系列达到相同的值,它们也会立即发散。
    【解决方案4】:

    使用以下代码返回一个短字符串:

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

    【讨论】:

    • 只返回十六进制字符:0-9 & A-F
    【解决方案5】:

    如果您运行的是 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
    

    【讨论】:

      【解决方案6】:

      我不是 T-SQL 专家,但我已经使用过的最简单的方法是:

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

      这会生成两个字符(A-Z,在 ascii 65-90 中)。

      【讨论】:

        【解决方案7】:
        select left(NEWID(),5)
        

        这将返回 guid 字符串最左边的 5 个字符

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

        【讨论】:

        • 虽然这个解决方案不适合生产系统,因为你会在几千左右之后很容易得到重复,但它对于在调试时快速简单地获取随机字符串非常有用或测试一些东西。
        • 我用它为大约 500 次登录生成了一个随机的 4 字母密码,这非常适合。是的,对于大数据和其他用途,请使用更多字符。
        • NEWID() 对于安全密码来说被认为不够随机,因此根据您的要求,您确实需要小心。使用 5 个字符,您会在大约 1500 条记录后发生冲突。在我的测试中,如果有 4 个字符,您会在 55-800 条记录中发生冲突。
        • @Ian1971 作为临时密码,还是可以的。假设我为您的 ATM 卡提供了 4 个字母的密码。同样的密码也可能会发给第 800 个用户,但您使用他的卡和密码的可能性极小。这几乎是一个随机数,可以用于临时访问。
        • 让我们从右侧开始,在 UUID 中,第一个八个字符之后的连字符在那里,但从右侧开始,在 12 之后有连字符。所以,对(NEWID(), 10)
        【解决方案8】:

        对于一个随机字母,您可以使用:

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

        使用newid()rand() 之间的一个重要区别是,如果您返回多行,newid() 会为每一行单独计算,而rand() 会为整个查询计算一次。

        【讨论】:

          【解决方案9】:

          这是一个随机字母数字生成器

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

          【讨论】:

          • 只返回十六进制字符:0-9 & A-F
          【解决方案10】:

          有很多好的答案,但到目前为止,它们都不允许自定义字符池并用作列的默认值。我希望能够做这样的事情:

          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
          

          【讨论】:

            【解决方案11】:

            这对我很有用:我只需要为一个 ID 生成三个随机字母数字字符,但它可以适用于最长 15 个左右的任何长度。

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

            【讨论】:

            • 只返回十六进制字符:0-9 & A-F
            • 是的,我猜你是对的。它不是真正的“字母数字”,因为您没有得到字符 >“F”。
            【解决方案12】:

            我意识到这是一个老问题,有很多很好的答案。但是,当我发现这个时,我还发现了 Saeid Hasani 在 TechNet 上的一篇更新的文章

            T-SQL: How to Generate Random Passwords

            虽然解决方案侧重于密码,但它适用于一般情况。 Saeid 通过各种考虑来达成解决方案。很有启发性。

            包含文章中所有代码块的脚本可通过TechNet Gallery 单独获得,但我肯定会从文章开始。

            【讨论】:

              【解决方案13】:

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

              注意字节长度和编码大小不一样;使用this文章中的以下内容进行转换:

              Bytes = 3 * (LengthInCharacters / 4) - Padding
              

              【讨论】:

                【解决方案14】:

                我首先遇到了this blog post,然后想出了我在当前项目中使用的以下存储过程(对不起,奇怪的格式):

                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
                

                【讨论】:

                  【解决方案15】:

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

                  然后您可以简单地从视图中提取字符并根据需要连接它们。

                  编辑:受到斯蒂芬的回应的启发......

                  select top 1 RandomChar from tblRandomCharacters order by newid()
                  

                  不需要视图(事实上,我不确定我为什么要这样做 - 代码是几年前的)。您仍然可以指定要在表格中使用的字符。

                  【讨论】:

                    【解决方案16】:

                    我使用我开发的这个程序只是规定了您希望能够在输入变量中显示的字符,您也可以定义长度。 希望这种格式很好,我是堆栈溢出的新手。

                    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
                    

                    【讨论】:

                      【解决方案17】:

                      有时我们需要很多随机的东西:爱、善良、假期等等。 多年来,我收集了一些随机生成器,这些来自 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
                      

                      【讨论】:

                      • 感谢发布方便的随机生成器集合
                      【解决方案18】:

                      这将产生一个长度为 96 个字符的字符串,来自 Base64 范围(大写、小写、数字、+ 和 /)。添加 3 个“NEWID()”会将长度增加 32,没有 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
                      

                      如果您将其应用于一个集合,请确保从该集合中引入一些东西,以便重新计算 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
                      

                      【讨论】:

                        【解决方案19】:

                        创建或更改过程 USP_GENERATE_RANDOM_CHARACTER(@NO_OF_CHARS INT,@RANDOM_CHAR VARCHAR(40) 输出) 作为 开始

                        SELECT @RANDOM_CHAR  = SUBSTRING (REPLACE(CONVERT(VARCHAR(40), NEWID()), '-',''), 1, @NO_OF_CHARS)
                        

                        结束 /* 用法: 声明@OUT VARCHAR(40) 执行 USP_GENERATE_RANDOM_CHARACTER 13,@RANDOM_CHAR = @OUT 输出 选择@OUT */

                        【讨论】:

                        • 请为这段代码的作用添加一些解释
                        • 它使用“guid 的子字符串”方法。 GUID 是相当随机的,因此您也可以得到任意长度的随机字母数字字符串。
                        【解决方案20】:

                        Remus Rusanu 代码的小修改-感谢分享

                        这会生成一个随机字符串,可以在没有种子值的情况下使用

                           declare @minLen int = 1, @maxLen int = 612, @string varchar(8000);
                        
                        declare @length int; 
                        declare @seed INT 
                        declare @alpha varchar(8000)
                            , @digit varchar(8000)
                            , @specials varchar(8000)
                            , @first varchar(8000)
                        declare @step bigint = rand() * 2147483647;
                        
                        
                        select @alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
                            , @digit = '1234567890'
                            , @specials = '_@#-/\ '
                        select @first = @alpha + '_@';
                        
                        set  @seed = (rand(@step)*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 
                            SELECT @string 
                        

                        【讨论】:

                          【解决方案21】:

                          这是基于新 ID 的东西。

                          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 & A-F
                          【解决方案22】:

                          我想我会分享或回馈社区... 它是基于 ASCII 的,解决方案并不完美,但效果很好。 享受, 戈兰 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
                          

                          【讨论】:

                          • 我确实意识到我的解决方案不是随机字符生成,而是可预测的字符串混淆...... :)
                          【解决方案23】:

                          这与其他答案之一一样使用带有种子的 rand,但没有必要在每次调用时都提供种子。在第一次调用时提供它就足够了。

                          这是我修改后的代码。

                          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
                          

                          【讨论】:

                            【解决方案24】:

                            SQL Server 2012+ 中,我们可以连接一些 (G)UID 的二进制文件,然后对结果进行 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))

                            【讨论】:

                              【解决方案25】:

                              这是我今天想出的一个(因为我不太喜欢任何现有的答案)。

                              这个生成一个随机字符串的临时表,基于newid(),但也支持自定义字符集(所以不仅仅是0-9和AF),自定义长度(最多255,限制很难-编码,但可以更改),以及自定义数量的随机记录。

                              这是源代码(希望 cmets 帮助):

                              /**
                               * 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())和过于复杂(即自定义随机数算法)。

                              它也很短(减去 cmets),并且易于理解(至少对我而言),这在我的书中一直是一个优点。

                              但是,此方法无法播种,因此每次都是真正随机的,您将无法以任何可靠的方式复制同一组数据。 OP 没有将其列为要求,但我知道有些人会寻找这种东西。

                              我知道我在这里聚会迟到了,但希望有人会觉得这很有用。

                              【讨论】:

                                【解决方案26】:

                                根据本文中的各种有用回复,我结合了我喜欢的几个选项。

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

                                【讨论】:

                                  【解决方案27】:

                                  以下是在 SQL 中生成 4 或 8 个字符长的随机字母数字字符串的方法

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

                                   SELECT RIGHT(REPLACE(CONVERT(VARCHAR(36),NEWID()),'-',''),8)
                                  

                                  【讨论】:

                                    【解决方案28】:

                                    所以我喜欢上面的很多答案,但我一直在寻找一些更随机的东西。我还想要一种显式调用排除字符的方法。下面是我使用调用CRYPT_GEN_RANDOM 来获取加密随机数的视图的解决方案。在我的示例中,我只选择了一个 8 字节的随机数。请注意,您可以增加此大小,也可以根据需要使用函数的种子参数。这是文档的链接: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,'!,@,#,$,%,&,?');
                                    

                                    【讨论】:

                                      【解决方案29】:

                                      它非常简单。使用它并享受它。

                                      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;
                                      

                                      【讨论】:

                                      猜你喜欢
                                      • 2013-10-18
                                      • 2017-08-01
                                      • 2011-06-30
                                      • 1970-01-01
                                      • 1970-01-01
                                      • 2011-04-01
                                      相关资源
                                      最近更新 更多