Якщо ви хочете сформувати псевдослучайний буквено-цифровий рядок за допомогою T-SQL, як би ви це зробили? Як би ви виключили з них символи, такі як знаки долара, тире та скісні риски?
Якщо ви хочете сформувати псевдослучайний буквено-цифровий рядок за допомогою T-SQL, як би ви це зробили? Як би ви виключили з них символи, такі як знаки долара, тире та скісні риски?
Відповіді:
При генерації випадкових даних, спеціально для тестування, дуже корисно зробити дані випадковими, але відтворюваними. Секрет полягає у використанні явного насіння для випадкової функції, щоб при повторному запуску тесту з тим самим насінням він знову виробляв точно такі самі рядки. Ось спрощений приклад функції, яка генерує імена об’єктів відтворюваним чином:
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;
Оновлення 17.02.2016: Дивіться коментарі нижче, вихідна процедура мала проблему в способі просування випадкового насіння. Я оновив код, а також виправив згадану окрему проблему.
@seed = rand(@seed+1)*2147483647
. Для значення 529126 наступним значенням є 1230039262, а потім наступним значенням - 192804. послідовність продовжується ідентично після цього. Вихідні дані повинні відрізнятися першим символом, але це не так через помилку, яка виключає один: SUBSTRING(..., 0, ...)
повертає порожній рядок для індексу 0, а для 529126 це "приховує" перший генерований символ. Виправлення полягає в обчисленні @dice = rand(@seed) * len(@specials)+1
для створення індексів 1 на основі.
+1
в rand(@seed+1)
бути сам по собі випадковим і визначається тільки з вихідного насіннєвого матеріалу . Таким чином, навіть якщо серії мають однакове значення, вони негайно розходяться.
За допомогою гіда
SELECT @randomString = CONVERT(varchar(255), NEWID())
дуже короткий ...
Подібно до першого прикладу, але з більшою гнучкістю:
-- 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
Я забув згадати одну з інших особливостей, яка робить це більш гнучким. Повторюючи блоки символів у @CharPool, ви можете збільшити зважування певних символів, щоб з більшою ймовірністю їх було вибрано.
CONVERT(int, RAND() * @PoolLength) + 1
(зверніть увагу на доданий +1). SUBSTRING у T-SQL починається з індексу 1, тому ця функція іноді додає порожній рядок (коли індекс дорівнює 0) і ніколи не додає останній символ із @CharPool
.
Якщо ви використовуєте 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
select left(NEWID(),5)
Це поверне 5 лівих символів лідируючого рядка
Example run
------------
11C89
9DB02
Ось випадковий алфавітно-цифровий генератор
print left(replace(newid(),'-',''),@length) //--@length is the length of random Num.
Для одного випадкового листа ви можете використовувати:
select substring('ABCDEFGHIJKLMNOPQRSTUVWXYZ',
(abs(checksum(newid())) % 26)+1, 1)
Важливою відмінністю використання в newid()
порівнянні з rand()
тим, що якщо ви повертаєте кілька рядків, newid()
обчислюється окремо для кожного рядка, тоді rand()
як обчислюється один раз для всього запиту.
Це працювало для мене: мені потрібно було генерувати лише три випадкові буквено-цифрові символи для ідентифікатора, але він міг працювати будь-якої довжини до 15 або близько того.
declare @DesiredLength as int = 3;
select substring(replace(newID(),'-',''),cast(RAND()*(31-@DesiredLength) as int),@DesiredLength);
Є багато хороших відповідей, але поки що жодна з них не дозволяє настроюваний пул символів і працює як значення за замовчуванням для стовпця. Я хотів мати можливість зробити щось подібне:
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
Я усвідомлюю, що це старе запитання з багатьма чудовими відповідями. Однак, коли я знайшов це, я також знайшов недавню статтю про TechNet Саїда Хасані
T-SQL: Як генерувати випадкові паролі
Хоча рішення зосереджується на паролях, воно застосовується до загального випадку. Саїд працює з різними міркуваннями, щоб знайти рішення. Це дуже повчально.
Сценарій, що містить усі блоки коду з статті, доступний окремо через галерею TechNet , але я б точно розпочав із статті.
Я використовую цю процедуру, яку я розробив, просто закріпивши символи, які ви хочете мати можливість відображати у вхідних змінних, ви також можете визначити довжину. Сподіваюся, що ці формати добре, я новачок у стеку.
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
Іноді нам потрібно багато випадкових речей: любов, доброта, відпустка і т. Д. За ці роки я зібрав кілька генераторів випадкових випадків, і це від Піналь Дейва та відповіді на 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
Ось одна, яку я сьогодні придумав (бо жодна з існуючих відповідей мені не сподобалась).
Це генерує тимчасову таблицю випадкових рядків, засновану на newid()
, але також підтримує власний набір символів (тому більше, ніж просто 0-9 & AF), спеціальну довжину (до 255, обмеження жорстко закодовано, але може бути змінено) та спеціальну кількість випадкових записів.
Ось вихідний код (сподіваємось, коментарі допоможуть):
/**
* 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 секунди, щоб згенерувати 1000 результатів довжиною 60, що більше, ніж мені коли-небудь знадобиться особисто), що було одним із моїх перших занепокоєнь з усіх мутацій рядків, які я роблю.
Головним виводом тут є те, що я не намагаюся створити власний генератор випадкових чисел, і мій набір символів не обмежений. Я просто використовую генератор випадкових випадків, який має SQL (я знаю, що існуєrand()
, але це не чудово для результатів таблиць). Сподіваємось, такий підхід поєднує тут два типи відповідей - надто прості (тобто справедливіnewid()
) та надмірно складних (тобто користувальницьких алгоритмів випадкових чисел).
Це також коротко (за мінусом коментарів) і легко зрозуміле (принаймні для мене), що завжди є плюсом у моїй книзі.
Однак цей метод не можна засіяти, тому він буде справді випадковим кожного разу, і ви не зможете відтворити один і той же набір даних із будь-якими засобами надійності. OP не вказав це як вимогу, але я знаю, що деякі люди шукають подібні речі.
Я знаю, що запізнився на вечірку тут, але, сподіваюся, комусь це знадобиться.
Для 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))
Зверніть увагу, що довжина байта не така, як закодований розмір; скористайтеся наступним із цієї статті для перетворення:
Bytes = 3 * (LengthInCharacters / 4) - Padding
Спочатку я натрапив на цю публікацію в блозі , а потім придумав наступну збережену процедуру для цього, яку я використовую в поточному проекті (вибачте за дивне форматування):
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
Я зробив це в 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
Тоді ви можете просто витягнути символи з подання та об’єднати їх за потреби.
EDIT: Натхненний відповіддю Стефана ...
select top 1 RandomChar from tblRandomCharacters order by newid()
Не потрібно перегляду (насправді я не впевнений, чому я це зробив - код ще кілька років тому). Ви все ще можете вказати символи, які ви хочете використовувати в таблиці.
Ось щось на основі 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)
Я думав поділитися або повернути спільноті ... Він заснований на ASCII, і рішення не ідеальне, але воно працює досить добре. Насолоджуйтесь, Горан Б.
/*
-- 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
Тут використовується 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
У 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))
в підвибірці.
Тому мені сподобалось багато відповідей вище, але я шукав щось, що мало б трохи випадковіший характер. Я також хотів спосіб явно викликати виключені символи. Нижче наведено моє рішення з використанням подання, яке викликає, 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,'!,@,#,$,%,&,?');
~ На ура
це дуже просто. використовуйте це і насолоджуйтесь.
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;
Це дасть рядок довжиною 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