【发布时间】:2019-12-11 11:30:20
【问题描述】:
在Zohar Answer 的帮助下,我得到了生成随机字符串的 SQL 函数,但我遇到了重复的问题。
查询
Create FUNCTION [dbo].[MaskGenerator]
(
@Prefix nvarchar(4000), -- use null or an empty string for no prefix
@suffix nvarchar(4000), -- use null or an empty string for no suffix
@MinLength int, -- the minimum length of the random part
@MaxLength int, -- the maximum length of the random part
@Count int, -- the maximum number of rows to return. Note: up to 1,000,000 rows
@CharType tinyint -- 1, 2 and 4 stands for lower-case, upper-case and digits.
-- a bitwise combination of these values can be used to generate all possible combinations:
-- 3: lower and upper, 5: lower and digis, 6: upper and digits, 7: lower, upper nad digits
)
RETURNS TABLE
AS
RETURN
-- An inline tally table with 1,000,000 rows
WITH E1(N) AS (SELECT N FROM (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) V(N)), -- 10
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --100
E3(N) AS (SELECT 1 FROM E2 a, E2 b), --10,000
Tally(N) AS (SELECT ROW_NUMBER() OVER (ORDER BY @@SPID) FROM E3 a, E2 b) --1,000,000
SELECT TOP(@Count) N As Number,
CONCAT(@Prefix, (
SELECT TOP (Length)
-- choose what char combination to use for the random part
CASE @CharType
WHEN 1 THEN LOWER
WHEN 2 THEN UPPER
WHEN 3 THEN IIF(Rnd % 2 = 0, LOWER, UPPER)
WHEN 4 THEN Digit
WHEN 5 THEN IIF(Rnd % 2 = 0, LOWER, Digit)
WHEN 6 THEN IIF(Rnd % 2 = 0, UPPER, Digit)
WHEN 7 THEN
CASE Rnd % 3
WHEN 0 THEN LOWER
WHEN 1 THEN UPPER
ELSE Digit
END
END
FROM Tally As T0
-- create a random number from the guid using the GuidGenerator view
CROSS APPLY (SELECT ABS(CHECKSUM(NewGuid)) As Rnd FROM GuidGenerator) AS RAND
CROSS APPLY
(
-- generate a random lower-case char, upper-case char and digit
SELECT CHAR(97 + Rnd % 26) As LOWER, -- Random lower case letter
CHAR(65 + Rnd % 26) As UPPER,-- Random upper case letter
CHAR(48 + Rnd % 10) As Digit -- Random digit
) AS Chars
WHERE T0.N <> -T1.N -- Needed for the subquery to get re-evaluated for each row
FOR XML PATH('')
), @Suffix) As RandomString
FROM Tally As T1
CROSS APPLY
(
-- Select a random length between @MinLength and @MaxLength (inclusive)
SELECT TOP 1 N As Length
FROM Tally As T2
CROSS JOIN GuidGenerator
WHERE T2.N >= @MinLength
AND T2.N <= @MaxLength
AND T2.N <> t1.N
ORDER BY NewGuid
) As Lengths;
上述函数将根据其参数提供随机字符串。例如下面的查询将生成 100 个随机字符串,格式为 Test_Product_。结果集具有需要忽略的重复值。我已尝试应用 row_number,但它会降低查询性能,同时请求计数也不会到来。
SELECT * FROM dbo.MaskGenerator('Test_Product_',null,1,4,100,4) ORDER BY 2
我在这里做了小提琴演示:SQL Fiddle 我的尝试也是 here
【问题讨论】:
-
为什么不直接使用
newid()? -
@GordonLinoff 因为 OP 希望控制生成的字符串的格式 - 长度、允许的字符、前缀和后缀。
-
如果你在这样的函数中使用 newid() 你会得到一个关于使用副作用运算符的错误。我猜 MS 期望这些函数是确定性的。带有 newid 的视图基本上是一种解决方法。
-
@LukStorms 我不认为这是关于函数的确定性,而是关于
NewId()函数的副作用 - 但是是的 - 视图只是一种解决方法。
标签: sql sql-server unique distinct