Generando cadenas aleatorias con T-SQL

96

Si quisiera generar una cadena alfanumérica pseudoaleatoria usando T-SQL, ¿cómo lo haría? ¿Cómo excluiría caracteres como signos de dólar, guiones y barras?

Scott Lawrence
fuente

Respuestas:

40

Al generar datos aleatorios, especialmente para pruebas, es muy útil hacer que los datos sean aleatorios, pero reproducibles. El secreto es utilizar semillas explícitas para la función aleatoria, de modo que cuando la prueba se vuelva a ejecutar con la misma semilla, vuelva a producir exactamente las mismas cadenas. A continuación, se muestra un ejemplo simplificado de una función que genera nombres de objeto de manera reproducible:

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

Cuando se ejecutan las pruebas, la persona que llama genera una semilla aleatoria que asocia con la ejecución de la prueba (la guarda en la tabla de resultados) y luego la pasa a lo largo de la semilla, similar a esta:

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;  

Actualización 2016-02-17: vea los comentarios a continuación, el procedimiento original tenía un problema en la forma en que avanzaba la semilla aleatoria. Actualicé el código y también solucioné el problema mencionado uno por uno.

Remus Rusanu
fuente
Tenga en cuenta que la resiembra en mi ejemplo es principalmente para ilustrar el punto. En la práctica, basta con sembrar el RNG una vez por sesión, siempre que la secuencia de llamada sea determinista después.
Remus Rusanu
2
Sé que este es un hilo antiguo, pero el código devuelve la misma cadena para la semilla 192804 y 529126
davey
1
@RemusRusanu también me interesaría una respuesta al comentario de davey
l --''''''--------- '' '' '' '' '' ''
La semilla avanza con la fórmula @seed = rand(@seed+1)*2147483647. Para el valor 529126, el siguiente valor es 1230039262 y luego el siguiente valor es 192804. La secuencia continúa idénticamente después de esto. La salida debe diferir por el primer carácter, pero no debido a un error de uno por uno: SUBSTRING(..., 0, ...)devuelve una cadena vacía para el índice 0 y para 529126 esto 'oculta' el primer carácter generado. La solución es calcular @dice = rand(@seed) * len(@specials)+1para hacer los índices basados ​​en 1.
Remus Rusanu
Este problema surge del hecho de que las series aleatorias, una vez que alcanzan un valor común, progresan de manera idéntica. Si es necesario, se puede evitar haciendo que el +1in the rand(@seed+1)sea ​​aleatorio y determinado solo a partir de la semilla inicial. De esta forma, incluso si la serie alcanza el mismo valor, divergen inmediatamente.
Remus Rusanu
202

Usando una guía

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

muy corto ...

Stefan Steinegger
fuente
7
+1, muy fácil. Combine con DERECHA / SUBSTRING para truncarlo a la longitud necesaria.
Johannes Rudolph
2
Me gusta esto, dulce y simple. Aunque no tiene la función de "predictibilidad", funciona muy bien para la generación de datos.
madhurtanwani
6
¡ No use RIGHT / SUBSTRING para truncar un UUID ya que no será único ni aleatorio debido a la forma en que se generan los UUID!
ooxi
1
Esto es bueno, pero si usa esto, asegúrese de leer esto: blogs.msdn.com/b/oldnewthing/archive/2008/06/27/8659071.aspx - Nota GUID es la implementación de Microsoft de UUID, independientemente del punto ooxi, debe tener cuidado al cortar el UUID.
Clarence Liu
7
Los caracteres alfa en NEWID () son hexadecimales, por lo que solo obtiene AF, no el resto del alfabeto. es limitante en ese sentido.
smoore4
52

Similar al primer ejemplo, pero con más flexibilidad:

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

Olvidé mencionar una de las otras características que hace que esto sea más flexible. Al repetir bloques de caracteres en @CharPool, puede aumentar la ponderación de ciertos caracteres para que sea más probable que se elijan.

Chris Judge
fuente
1
+1 es una buena rutina, la estoy usando como parte de un procedimiento almacenado.
Marcello Miorelli
2
Buena solucion. Desafortunadamente, no funciona dentro de un udf. Por alguna razón, da este error: "Uso no válido de un operador de efectos secundarios 'rand' dentro de una función".
rdans
12
Hay un error en esta función en la llamada SUBSTRING (). Debería ser CONVERT(int, RAND() * @PoolLength) + 1(tenga en cuenta el +1 añadido). SUBSTRING en T-SQL comienza con el índice 1, por lo que, tal como está, esta función a veces agrega una cadena vacía (cuando el índice es 0) y nunca agrega el último carácter del @CharPool.
Dana Cartwright
@Dana Cartwright, ¿podría ayudarme con mi requisito? En la parte delantera, proporcionaré el número total de números de serie que se generarán, como 100 o 200, utilizando Charpool. La longitud mínima del número de serie será 10 y máxima 14 (que también se proporciona como parámetro)
Prathap Gangireddy
@PrathapGangireddy, haga una pregunta, los comentarios a una persona no son el lugar adecuado para las preguntas
Dana Cartwright
31

Utilice el siguiente código para devolver una cadena corta:

SELECT SUBSTRING(CONVERT(varchar(40), NEWID()),0,9)
Escarcha
fuente
2
Solo devuelve caracteres hexadecimales: 0-9 y AF
jumxozizi
19

Si está ejecutando SQL Server 2008 o superior, puede usar la nueva función criptográfica crypt_gen_random () y luego usar la codificación base64 para convertirla en una cadena. Esto funcionará para hasta 8000 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
fuente
16

No soy un experto en T-SQL, pero la forma más sencilla que ya he usado es así:

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

Esto genera dos caracteres (AZ, en ascii 65-90).

Henrique
fuente
13
select left(NEWID(),5)

Esto devolverá los 5 caracteres más a la izquierda de la cadena guid

Example run
------------
11C89
9DB02
Hammad Khan
fuente
1
Aunque esta solución no es buena para un sistema de producción, ya que obtendrá duplicados con bastante facilidad después de varios miles, es muy útil como una forma rápida y fácil de obtener una cadena aleatoria mientras se depura o prueba algo.
Ian1971
Usé esto para generar una contraseña aleatoria de 4 letras para unos 500 inicios de sesión y esto funciona perfecto para eso. Sí, para big data y otros fines, use más caracteres.
Hammad Khan
1
NEWID () no se considera lo suficientemente aleatorio para contraseñas seguras, por lo que, dependiendo de sus requisitos, debe tener cuidado. Con 5 caracteres obtienes una colisión después de aproximadamente 1500 registros. Con 4 caracteres obtienes una colisión entre 55 y 800 registros en mi prueba.
Ian1971
@ Ian1971 para contraseña temporal, todavía está bien. Digamos que le doy un pin de 4 letras para su tarjeta de cajero automático. Ese mismo pin podría ser emitido para, digamos, otro usuario número 800 también, pero las posibilidades de que use su tarjeta con su contraseña son muy poco probables. Es prácticamente un número aleatorio que está bien para acceso temporal.
Hammad Khan
6

Aquí hay un generador alfanumérico aleatorio

print left(replace(newid(),'-',''),@length) //--@length is the length of random Num.
Krishna Thota
fuente
Solo devuelve caracteres hexadecimales: 0-9 y AF
jumxozizi
5

Para una letra aleatoria, puede usar:

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

Una diferencia importante entre usar newid()versus rand()es que si devuelve varias filas, newid()se calcula por separado para cada fila, mientras que rand()se calcula una vez para toda la consulta.

Krubo
fuente
4

Esto funcionó para mí: necesitaba generar solo tres caracteres alfanuméricos aleatorios para una identificación, pero podría funcionar para cualquier longitud de hasta 15 más o menos.

declare @DesiredLength as int = 3;
select substring(replace(newID(),'-',''),cast(RAND()*(31-@DesiredLength) as int),@DesiredLength);
Brian
fuente
Solo devuelve caracteres hexadecimales: 0-9 y AF
jumxozizi
Sí, supongo que tienes razón. No es realmente "alfanumérico" ya que no obtienes caracteres> "F".
Brian
3

Hay muchas respuestas buenas, pero hasta ahora ninguna de ellas permite un grupo de caracteres personalizable y funciona como un valor predeterminado para una columna. Quería poder hacer algo como esto:

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

Entonces se me ocurrió esto. Tenga cuidado con el número 32 codificado de forma rígida si lo modifica.

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

Me doy cuenta de que esta es una vieja pregunta con muchas buenas respuestas. Sin embargo, cuando encontré esto, también encontré un artículo más reciente en TechNet por Saeid Hasani

T-SQL: Cómo generar contraseñas aleatorias

Si bien la solución se centra en las contraseñas, se aplica al caso general. Saeid trabaja a través de varias consideraciones para llegar a una solución. Es muy instructivo.

Un script que contiene todos los bloques de código del artículo está disponible por separado a través de la Galería de TechNet , pero definitivamente comenzaría con el artículo.

Karl Kieninger
fuente
1

Utilizo este procedimiento que desarrollé simplemente para estipular los caracteres que desea poder mostrar en las variables de entrada, también puede definir la longitud. Espero que este formato esté bien, soy nuevo en el desbordamiento de pila.

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
Base de datosDave
fuente
1

A veces necesitamos muchas cosas aleatorias: amor, amabilidad, vacaciones, etc. He recopilado algunos generadores aleatorios a lo largo de los años, y estos son de Pinal Dave y una respuesta de stackoverflow que encontré una vez. Refs debajo.

--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
fuente
1
Gracias por publicar una colección de prácticos generadores aleatorios
iiminov
1

Aquí hay uno que se me ocurrió hoy (porque no me gustó lo suficiente ninguna de las respuestas existentes).

Éste genera una tabla temporal de cadenas aleatorias, se basa en newid(), pero también admite un conjunto de caracteres personalizado (por lo tanto, más que solo 0-9 y AF), longitud personalizada (hasta 255, el límite está codificado de forma rígida, pero puede ser cambiado) y un número personalizado de registros aleatorios.

Aquí está el código fuente (con suerte, los comentarios ayudan):

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

No es un procedimiento almacenado, pero no sería tan difícil convertirlo en uno. Tampoco es terriblemente lento (me tomó ~ 0.3 segundos generar 1,000 resultados de longitud 60, que es más de lo que necesitaré personalmente), que fue una de mis preocupaciones iniciales de toda la mutación de cuerdas que estoy haciendo.

La conclusión principal aquí es que no estoy tratando de crear mi propio generador de números aleatorios, y mi conjunto de caracteres no está limitado. Simplemente estoy usando el generador aleatorio que tiene SQL (sé que lo hay rand(), pero eso no es bueno para los resultados de la tabla). Es de esperar que este enfoque combine los dos tipos de respuestas aquí, desde demasiado simple (es decir, solonewid() ) y demasiado complejo (es decir, algoritmo de número aleatorio personalizado).

También es breve (menos los comentarios) y fácil de entender (al menos para mí), lo que siempre es una ventaja en mi libro.

Sin embargo, este método no se puede sembrar, por lo que será realmente aleatorio cada vez y no podrá replicar el mismo conjunto de datos con ningún medio de confiabilidad. El OP no incluyó eso como un requisito, pero sé que algunas personas buscan ese tipo de cosas.

Sé que llego tarde a la fiesta aquí, pero espero que alguien lo encuentre útil.

Auge
fuente
1

Para SQL Server 2016 y versiones posteriores, aquí hay una expresión realmente simple y relativamente eficiente para generar cadenas criptográficamente aleatorias de una longitud de byte determinada:

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

Tenga en cuenta que la longitud del byte no es la misma que el tamaño codificado; utilice lo siguiente de este artículo para convertir:

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

Primero encontré esta publicación de blog , luego se me ocurrió el siguiente procedimiento almacenado para esto que estoy usando en un proyecto actual (lo siento por el formato extraño):

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

Hice esto en SQL 2000 creando una tabla que tenía los caracteres que quería usar, creando una vista que selecciona los caracteres de esa tabla ordenados por newid () y luego seleccionando el primer carácter de esa vista.

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

...

SELECT TOP 1 CodeChar FROM dbo.vwCodeCharRandom

Luego, simplemente puede extraer caracteres de la vista y concatenarlos según sea necesario.

EDITAR: Inspirado por la respuesta de Stephan ...

select top 1 RandomChar from tblRandomCharacters order by newid()

No es necesario verlo (de hecho, no estoy seguro de por qué hice eso, el código es de varios años atrás). Aún puede especificar los caracteres que desea usar en la tabla.

Mayonesa
fuente
0

Aquí hay algo basado en 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
fuente
Solo devuelve caracteres hexadecimales: 0-9 y AF
jumxozizi
0

Pensé en compartir o retribuir a la comunidad ... Está basado en ASCII y la solución no es perfecta, pero funciona bastante bien. Disfruta, 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.
fuente
Me doy cuenta de que mi solución NO es la generación aleatoria de caracteres, sino una ofuscación predecible de cadenas ... :)
Goran B.
0

Esto usa rand con una semilla como una de las otras respuestas, pero no es necesario proporcionar una semilla en cada llamada. Proporcionarlo en la primera llamada es suficiente.

Este es mi 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
fuente
0

En SQL Server 2012+ , podríamos concatenar los binarios de algunos (G) UID y luego hacer una conversión en base64 en el 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
;

Si necesita cadenas más largas (o ve =caracteres en el resultado), debe agregar más + CAST(newid() as varbinary(max))en la sub-selección.

jumxozizi
fuente
0

Así que me gustaron muchas de las respuestas anteriores, pero estaba buscando algo que fuera un poco más aleatorio por naturaleza. También quería una forma de llamar explícitamente a los personajes excluidos. A continuación se muestra mi solución con una vista que llama a CRYPT_GEN_RANDOMpara obtener un número aleatorio criptográfico. En mi ejemplo, solo elegí un número aleatorio de 8 bytes. Tenga en cuenta que puede aumentar este tamaño y también utilizar el parámetro semilla de la función si lo desea. Aquí está el enlace a la documentación: 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];

La razón para crear la vista es porque CRYPT_GEN_RANDOM no se puede llamar directamente desde una función.

A partir de ahí, creé una función escalar que acepta una longitud y un parámetro de cadena que puede contener una cadena delimitada por comas de caracteres excluidos.

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

A continuación se muestra un ejemplo de cómo llamar a la función.

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

~ Saludos

Rogala
fuente
0

es muy simple úsalo y disfruta.

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

Esto producirá una cadena de 96 caracteres de longitud, desde el rango de Base64 (superiores, inferiores, números, + y /). Agregar 3 "NEWID ()" aumentará la longitud en 32, sin relleno 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

Si está aplicando esto a un conjunto, asegúrese de introducir algo de ese conjunto para que se vuelva a calcular el NEWID (); de lo contrario, obtendrá el mismo valor 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
fuente
0

Basado en varias respuestas útiles en este artículo, obtuve una combinación de un par de opciones que me gustaron.

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