Gerando strings aleatórias com T-SQL

95

Se você quisesse gerar uma string alfanumérica pseudo-aleatória usando T-SQL, como faria isso? Como você excluiria caracteres como cifrões, travessões e barras dele?

Scott Lawrence
fonte

Respostas:

40

Ao gerar dados aleatórios, especialmente para teste, é muito útil tornar os dados aleatórios, mas reproduzíveis. O segredo é usar sementes explícitas para a função aleatória, de modo que, quando o teste for executado novamente com a mesma semente, ele produza novamente exatamente as mesmas strings. Aqui está um exemplo simplificado de uma função que gera nomes de objetos de maneira reproduzível:

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

Ao executar os testes, o chamador gera uma semente aleatória que associa à execução do teste (salva na tabela de resultados) e, em seguida, repassa a semente, semelhante a este:

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;  

Atualização 2016-02-17: Veja os comentários abaixo, o procedimento original teve um problema na forma como avançou a semente aleatória. Eu atualizei o código e também corrigi o problema mencionado por defeito.

Remus Rusanu
fonte
Observe que a nova propagação em meu exemplo é principalmente para ilustrar o ponto. Na prática, é o suficiente para propagar o RNG uma vez por sessão, desde que a sequência de chamada seja determinística posteriormente.
Remus Rusanu
2
Eu sei que este é um tópico antigo, mas o código retorna a mesma string para seed 192804 e 529126
davey
1
@RemusRusanu eu também estaria interessado em uma resposta ao comentário de davey
l --'''''''--------- '' '' '' '' '' '' '17 de
A semente avança com a fórmula @seed = rand(@seed+1)*2147483647. Para o valor 529126, o próximo valor é 1230039262 e o próximo valor é 192804. a sequência continua de forma idêntica depois disso. A saída deve diferir pelo primeiro caractere, mas não é devido a um erro off-by-one: SUBSTRING(..., 0, ...)retorna uma string vazia para o índice 0 e para 529126 isso 'oculta' o primeiro caractere gerado. A correção é calcular @dice = rand(@seed) * len(@specials)+1para tornar os índices baseados em 1.
Remus Rusanu
Esse problema decorre do fato de que as séries aleatórias, uma vez que atingem um valor comum, progridem de forma idêntica. Se necessário, pode ser evitado tornando o próprio +1in no rand(@seed+1)ser aleatório e determinado apenas a partir da semente inicial. Dessa forma, mesmo que as séries atinjam o mesmo valor, elas divergem imediatamente.
Remus Rusanu de
202

Usando um guid

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

muito curto ...

Stefan Steinegger
fonte
7
1, muito fácil. Combine com RIGHT / SUBSTRING para truncar no comprimento necessário.
Johannes Rudolph de
2
Eu gosto disso - doce e simples. Embora não tenha o recurso de "previsibilidade", funciona muito bem para geração de dados.
madhurtanwani
6
Você não usar DIREITA / substring para truncar um UUID, uma vez que não será nem exclusiva nem aleatória devido à forma como UUIDs são gerados!
ooxi
1
Isso é bom, mas se você usar isso, certifique-se de ler: blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx - Observação GUID é a implementação do UUID da Microsoft, independentemente do ponto ooxi mencionado, você precisa ter cuidado como você corta o UUID.
Clarence Liu
7
Os caracteres alfa em NEWID () são hexadecimais, portanto, você obtém apenas AF, não o resto do alfabeto. é limitante nesse sentido.
smoore4 01 de
51

Semelhante ao primeiro exemplo, mas com mais flexibilidade:

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

Esqueci de mencionar um dos outros recursos que tornam isso mais flexível. Repetindo blocos de personagens em @CharPool, você pode aumentar o peso de certos personagens para que sejam mais propensos a serem escolhidos.

Chris Judge
fonte
1
+1 é uma boa rotina, estou usando como parte de um procedimento armazenado.
Marcello Miorelli,
2
Ótima solução. Infelizmente, não funciona dentro de um udf. Por alguma razão, ele apresenta este erro: "Uso inválido de um operador de efeito colateral 'rand' em uma função".
rdans
12
Há um bug nesta função na chamada SUBSTRING (). Deveria ser CONVERT(int, RAND() * @PoolLength) + 1(observe o +1 adicionado). SUBSTRING no T-SQL começa com o índice 1, portanto, do jeito que está, essa função às vezes adiciona uma string vazia (quando o índice é 0) e nunca adiciona o último caractere do @CharPool.
Dana Cartwright
@Dana Cartwright, você pode me ajudar com minhas necessidades .. e se eu precisar de comprimento constante de número de série usando o Charpool acima, como número de série alfanumérico 10 ou 12 alfanumérico. Como alteramos o procedimento armazenado. No front end, fornecerei o número total de números de série a serem gerados, como 100 ou 200, usando o Charpool. O comprimento mínimo do número de série será 10 e máximo 14 (que também é fornecido como parâmetro)
Prathap Gangireddy
@PrathapGangireddy por favor, faça uma pergunta, comentários para uma pessoa não são o lugar certo para perguntas
Dana Cartwright
31

Use o seguinte código para retornar uma string curta:

SELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9)
Escarcha
fonte
2
Retorna apenas caracteres hexadecimais: 0-9 e AF
jumxozizi 05 de
19

Se você estiver executando o SQL Server 2008 ou superior, poderá usar a nova função criptográfica crypt_gen_random () e, em seguida, usar a codificação base64 para torná-la uma string. Isso funcionará para até 8.000 caracteres.

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
Kevin O
fonte
16

Não sou especialista em T-SQL, mas a maneira mais simples que já usei é assim:

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

Isso gera dois caracteres (AZ, em ascii 65-90).

Henrique
fonte
11
select left(NEWID(),5)

Isso retornará os 5 caracteres mais à esquerda da string guia

Example run
------------
11C89
9DB02
Hammad Khan
fonte
1
Embora esta solução não seja boa para um sistema de produção, já que você obterá duplicatas facilmente depois de vários milhares ou mais, ela é bastante útil para uma maneira rápida e fácil de obter uma string aleatória enquanto depura ou testa algo.
Ian1971
Usei isso para gerar uma senha aleatória de 4 letras para cerca de 500 logins e isso funciona perfeitamente para isso. Sim, para big data e outros fins, use mais caracteres.
Hammad Khan
1
NEWID () não é considerado aleatório o suficiente para senhas seguras, portanto, dependendo de seus requisitos, você precisa ter cuidado. Com 5 caracteres, você obtém uma colisão após cerca de 1500 registros. Com 4 caracteres, você obtém uma colisão em qualquer lugar de 55-800 registros no meu teste.
Ian1971
@ Ian1971 para senha temporária, ainda está ok. Digamos que eu lhe dê um pino de 4 letras para seu cartão ATM. Esse mesmo pin pode ser emitido para, digamos, outro 800º usuário também, mas as chances de você usar o cartão dele com sua senha são altamente improváveis. É praticamente um número aleatório que pode ser acessado temporariamente.
Hammad Khan de
6

Aqui está um gerador alfa numérico aleatório

print left(replace(newid(),'-',''),@length) //--@length is the length of random Num.
Krishna Thota
fonte
Retorna apenas caracteres hexadecimais: 0-9 e AF
jumxozizi 05 de
5

Para uma letra aleatória, você pode usar:

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

Uma diferença importante entre usar newid()versus rand()é que se você retornar várias linhas, newid()é calculado separadamente para cada linha, enquanto rand()é calculado uma vez para a consulta inteira.

Krubo
fonte
4

Isso funcionou para mim: eu precisava gerar apenas três caracteres alfanuméricos aleatórios para um ID, mas poderia funcionar para qualquer comprimento de até 15 ou mais.

declare @DesiredLength as int = 3;
select substring(replace(newID(),'-',''),cast(RAND()*(31-@DesiredLength) as int),@DesiredLength);
Brian
fonte
Retorna apenas caracteres hexadecimais: 0-9 e AF
jumxozizi 05 de
Sim, acho que você está correto. Não é verdadeiramente "alfanumérico", pois você não obtém caracteres> "F".
Brian,
3

Existem muitas respostas boas, mas até agora nenhuma delas permite um conjunto de caracteres personalizável e funciona como um valor padrão para uma coluna. Eu queria ser capaz de fazer algo assim:

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

Então eu vim com isso. Cuidado com o número 32 embutido no código se você modificá-lo.

-- 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
default.kramer
fonte
2

Sei que essa é uma pergunta antiga com muitas respostas boas. No entanto, quando descobri isso, também encontrei um artigo mais recente no TechNet de Saeid Hasani

T-SQL: como gerar senhas aleatórias

Embora a solução se concentre em senhas, ela se aplica ao caso geral. Saeid trabalha com várias considerações para chegar a uma solução. É muito instrutivo.

Um script que contém todos os blocos de código do artigo está disponível separadamente na TechNet Gallery , mas eu definitivamente começaria pelo artigo.

Karl Kieninger
fonte
1

Eu utilizo este procedimento que desenvolvi simplesmente estipulando os caracteres que você deseja exibir nas variáveis ​​de entrada, você pode definir o comprimento também. Espero que este formato seja bom, sou novo no estouro de pilha.

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
DatabaseDave
fonte
1

Às vezes, precisamos de muitas coisas aleatórias: amor, gentileza, férias, etc. Eu colecionei alguns geradores aleatórios ao longo dos anos, e estes são de Pinal Dave e uma resposta stackoverflow que encontrei uma vez. Refs abaixo.

--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
Graeme
fonte
1
Obrigado por postar coleção de geradores aleatórios úteis
iiminov
1

Aqui está uma que pensei hoje (porque não gostei de nenhuma das respostas existentes o suficiente).

Este gera uma tabela temporária de strings aleatórias, baseada em newid() , mas também suporta um conjunto de caracteres personalizados (então mais do que apenas 0-9 e AF), comprimento personalizado (até 255, o limite é codificado, mas pode ser alterado) e um número personalizado de registros aleatórios.

Aqui está o código-fonte (espero que os comentários ajudem):

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

Não é um procedimento armazenado, mas não seria tão difícil transformá-lo em um. Também não é terrivelmente lento (levou cerca de 0,3 segundos para gerar 1.000 resultados de comprimento 60, o que é mais do que eu vou precisar pessoalmente), que foi uma das minhas preocupações iniciais de todas as mutações de string que estou fazendo.

A principal lição aqui é que não estou tentando criar meu próprio gerador de números aleatórios e meu conjunto de caracteres não é limitado. Estou simplesmente usando o gerador aleatório que o SQL tem (eu sei que existe rand(), mas isso não é ótimo para resultados de tabela). Esperançosamente, esta abordagem combina os dois tipos de respostas aqui, de simples demais (ou seja, apenasnewid() ) e excessivamente complexa (ou seja, algoritmo de número aleatório personalizado).

Também é curto (sem os comentários) e fácil de entender (pelo menos para mim), o que é sempre um ponto positivo no meu livro.

No entanto, esse método não pode ser propagado, então ele será realmente aleatório todas as vezes e você não poderá replicar o mesmo conjunto de dados com qualquer meio de confiabilidade. O OP não listou isso como um requisito, mas sei que algumas pessoas procuram esse tipo de coisa.

Sei que estou atrasado para a festa aqui, mas espero que alguém ache isso útil.

estrondo
fonte
0

Eu encontrei esta postagem do blog primeiro, então vim com o seguinte procedimento armazenado para isso que estou usando em um projeto atual (desculpe pela formatação estranha):

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
Scott Lawrence
fonte
0

Fiz isso no SQL 2000 criando uma tabela que tinha os caracteres que eu queria usar, criando uma visualização que seleciona os caracteres dessa tabela ordenados por newid () e, em seguida, selecionando o primeiro personagem dessa visualização.

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

...

SELECT TOP 1 CodeChar FROM dbo.vwCodeCharRandom

Em seguida, você pode simplesmente puxar os caracteres da visualização e concatená-los conforme necessário.

EDITAR: Inspirado na resposta de Stephan ...

select top 1 RandomChar from tblRandomCharacters order by newid()

Não há necessidade de uma visão (na verdade, não sei por que fiz isso - o código é de vários anos atrás). Você ainda pode especificar os caracteres que deseja usar na tabela.

maionese
fonte
0

Aqui está algo baseado em New 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)
Aamer Sattar
fonte
Retorna apenas caracteres hexadecimais: 0-9 e AF
jumxozizi 05 de
0

Pensei em compartilhar ou retribuir à comunidade ... É baseado em ASCII, e a solução não é perfeita, mas funciona muito bem. Aproveite, 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.
fonte
eu percebo que minha solução NÃO é a geração aleatória de caracteres, mas sim a previsível ofuscação de strings ... :)
Goran B.
0

Isso usa rand com uma semente como uma das outras respostas, mas não é necessário fornecer uma semente em cada chamada. Fornecê-lo na primeira chamada é suficiente.

Este é o meu código modificado.

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
Bryan M
fonte
0

No SQL Server 2012+ , poderíamos concatenar os binários de alguns (G) UIDs e, em seguida, fazer uma conversão de base64 no resultado.

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
;

Se você precisar de strings mais longas (ou se vir =caracteres no resultado), será necessário adicionar mais + CAST(newid() as varbinary(max))na sub seleção.

Jumxozizi
fonte
0

Então, gostei muito das respostas acima, mas estava procurando por algo que fosse um pouco mais aleatório por natureza. Eu também queria uma maneira de chamar explicitamente os personagens excluídos. Abaixo está minha solução usando um modo de exibição que chama o CRYPT_GEN_RANDOMpara obter um número aleatório criptográfico. No meu exemplo, escolhi apenas um número aleatório de 8 bytes. Observe que você pode aumentar esse tamanho e também utilizar o parâmetro semente da função, se desejar. Aqui está o link para a documentação: 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];

A razão para criar a visualização é porque CRYPT_GEN_RANDOMnão pode ser chamada diretamente de uma função.

A partir daí, criei uma função escalar que aceita um comprimento e um parâmetro de string que pode conter uma string delimitada por vírgulas de caracteres excluídos.

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

Abaixo está um exemplo de como chamar a função.

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

~ Saúde

Rogala
fonte
0

é muito simples. use-o e aproveite.

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;
Jbabaei
fonte
0

Isso produzirá uma string de 96 caracteres de comprimento, do intervalo Base64 (superiores, inferiores, números, + e /). Adicionar 3 "NEWID ()" aumentará o comprimento em 32, sem preenchimento 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

Se você estiver aplicando isso a um conjunto, certifique-se de introduzir algo desse conjunto para que o NEWID () seja recalculado, caso contrário, você obterá o mesmo valor a cada vez:

  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
Graham
fonte
0

Para SQL Server 2016 e posterior, aqui está uma expressão realmente simples e relativamente eficiente para gerar strings criptograficamente aleatórias de um determinado comprimento de byte:

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

Observe que o comprimento do byte não é igual ao tamanho codificado; use o seguinte deste artigo para converter:

Bytes = 3 * (LengthInCharacters / 4) - Padding
N8allan
fonte
0

Com base em várias respostas úteis neste artigo, cheguei a uma combinação de algumas opções de que gostei.

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