2016-06-02 4 views
0

Проблемы описана ниже:Как генерировать случайные буквенно-цифровые уникальные символы с заданной длиной

  1. Создать уникальные буквенно-цифровые символы.
  2. Длина символов должна быть 32.
  3. Уникальные номера могут быть посеяны в текущее время, чтобы помочь в уникальности сгенерированных чисел.
  4. буквенные знаки должны исходить из этого пула: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

Пример вывод: 445rpxlKYPkj1pg4q8nAy7Ab91zxZ8v1

Я могу сделать это с помощью Java, но буду очень признателен, если вы могли бы помочь мне сделать это на MS SQL или Т- SQL.

+2

Посмотрите на функцию [ 'CRYPT_GEN_RANDOM'] (https://msdn.microsoft.com/en-us/library/cc627408.aspx), который может генерировать определенное количество случайных байт, до 8000 . SELECT CONVERT (varchar (32), CRYPT_GEN_RANDOM (16), 2) 'вернет достаточно случайную строку. Однако у него не будет символов алфавита, кроме A-F. –

ответ

3

Во-первых, вам нужно разделить строку на отдельные строки. Затем сделайте SELECT с ORDER BY NEWID() для случайного сортировки. И, наконец, использовать FOR XML PATH('') конкатенировать их обратно:

DECLARE @str VARCHAR(100) = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 

;WITH E1(N) AS(-- 10^1 = 10 rows 
    SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N) 
), 
E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10^2 = 100 rows 
E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10^4 = 10,000 rows 
CteTally(N) AS(
    SELECT TOP(LEN(@str)) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) 
    FROM E4 
) 
SELECT (
    SELECT TOP(32) 
     SUBSTRING(@str, N, 1) 
    FROM CteTally t 
    ORDER BY NEWID() 
    FOR XML PATH('') 
) AS Result 

ONLINE DEMO

выше является более общего генератора случайных строк. Вы можете изменить его в соответствии с вашими потребностями. Если требование не изменится, вы можете просто использовать это:

DECLARE @str VARCHAR(100) = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
;WITH E1(N) AS(-- 52 Rows 
    SELECT 1 FROM(VALUES 
     (1),(1),(1),(1),(1),(1),(1),(1),(1),(1), 
     (1),(1),(1),(1),(1),(1),(1),(1),(1),(1), 
     (1),(1),(1),(1),(1),(1),(1),(1),(1),(1), 
     (1),(1),(1),(1),(1),(1),(1),(1),(1),(1), 
     (1),(1),(1),(1),(1),(1),(1),(1),(1),(1), 
     (1),(1) 
    )t(N) 
), 
CteTally(N) AS(
    SELECT ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) 
    FROM E1 
) 
SELECT (
    SELECT TOP(32) 
     SUBSTRING(@str, N, 1) 
    FROM CteTally t 
    ORDER BY NEWID() 
    FOR XML PATH('') 
) AS Result 
+0

Спасибо за решение @Felix попробует. –

1

Я сделал это родовые достаточно, чтобы справиться с любым пулом символов и любой длиной вывода. Основная идея состоит в том, чтобы принять произвольную последовательность байтов и использовать базовый алгоритм преобразования, чтобы преобразовать длинное число в новое представление, а затем перевести на строку, используя ваши нужные символы в качестве «цифр».

Для вашего конкретного сценария нам нужно около 183 бит, или log2 (52) x 32, чтобы добраться до нужной длины. Использование newid() будет генерировать уникальную последовательность бит, но это будет делать только 128 бит за раз, и ряд значений просто объединяется до тех пор, пока их не будет достаточно. Затем, имея значение для работы, основной цикл - это по сути то же самое длинное разделение, которое мы узнали из начальной школы. Промежуточные вычисления сохраняются на месте в массиве varbinary, и цикл продолжается только до тех пор, пока не будет получено достаточное количество выходных символов. Каждая итерация определяет другую младшую цифру в новой базе, и это может закончиться раньше, так как они не изменятся. Алгоритм не может гарантировать любую глобальную уникальность, если выход не потребляет по крайней мере все один newid(), поэтому убедитесь, что log2 (Len (бассейн)) длина х выход составляет по меньшей мере 128.

целевых базового , который в конечном счете является длиной пула символов, не может быть больше 256. Я жестко закодировал ограничение, установив максимальную длину в 128 байт @e. Для вопроса @e требуется всего 32 байта, и его можно было бы отрегулировать вверх или вниз по мере необходимости или просто определить как varbinary(max). Если вам нужно что-то более случайное, вы можете найти другой источник для энтропийных битов, например crypt_gen_random(). Поскольку уникальность, по-видимому, является основной проблемой, этот ответ соответствует этому требованию. И, кстати, повторение персонажей в бассейне, естественно, откроет дверь для столкновений.

Это быстро и универсально, и его можно легко обернуть функцией. И более надежная реализация будет обрабатывать эти дополнительные проверки.

declare @characterPool varchar(256) = 
    'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; 
declare @outputLength int = 32; 

declare @n int = 0; /* counter */ 
declare @numLoops int = ceiling(log(len(@characterPool))/log(2) * @outputLength/128) 
declare @e varbinary(128) = 0x; /* entropy */ 

while @n < @numLoops 
begin 
    set @e = cast(newid() as binary(16)); set @n += 1; 
end 

declare @b int; /* byte */ 
declare @d int; /* dividend */ 
declare @out varchar(128) = ''; 

declare @outputBase int = len(@characterPool); 
declare @entropyBytes int = len(@e); 

declare @m int = 0; 
while @m < @outputLength 
begin 
    set @b = 0; set @d = 0; set @n = 0; 
    while @n < @entropyBytes /* big-endian */ 
    begin 
     set @b = (@b - @d * @outputBase) * 256 + cast(substring(@e, @n + 1, 1) as int); 
     set @d = @b/@outputBase; 
     set @e = cast(stuff(@e, @n + 1, 1, cast(@d as binary(1))) as varbinary(128)); 
     set @n += 1; 
    end 
    set @out = substring(@characterPool, @b - @d * @outputBase + 1, 1) + @out; 
    set @m += 1; 
end 

select @out as "UniqueString" 

http://rextester.com/EYAK79470

В качестве одного из простого теста алгоритма можно просто присвоить известное значение в шестнадцатеричном формате и подтвердить, что выход (с использованием ABCDEF в бассейне символов) одно и то же шестнадцатеричное значение. Точно так же это, очевидно, работает с base64, двоичным и восьмеричным.

Обновление: Основная петля может быть выполнена быстрее, не перебирая больше байтов, чем необходимо. Я не знаю, как crypt_gen_random() сравнивается с newid() с точки зрения скорости или использования ЦП, поэтому это изменение может даже не быть положительным, поэтому я просто замечу его как альтернативу исследованию. Вам нужно будет оставить байты с newid на маленьком конце и прикрепить остальных к фронту.

declare @e varbinary(1024) = cast(newid() as binary(16)); 
declare @padBytes int = ceiling(log(len(@characterPool))/log(2) * @outputLength) - 128; 
if @padBytes > 0 set @e = crypt_gen_random(@padBytes) + @e; /* big end plus little end */ 
+0

Существует функция ['CRYPT_GEN_RANDOM'] (https://msdn.microsoft.com/en-us/library/cc627408.aspx), которая может генерировать заданное количество случайных байтов, до 8000. Нет необходимости вызовите 'NEWID' в цикле. –

+0

@ Владимир Я отметил это в своих комментариях о уникальности. Вероятности столкновения явно низки в любом случае, но причина использования 'newid()' заключается в том, чтобы * гарантировать *, что 128 бит глобально уникальны, но многое в конечном итоге является реальной проблемой. – shawnt00

+0

Я вижу. Я недостаточно внимательно изучил этот вопрос. Я не думал об уникальности, я был сосредоточен на случайности. –

Смежные вопросы