Ull*_*las 3 sql sql-server sql-server-2008
我正在寻找生成一个随机数,生成的数字不在另一个表上.
例如:如果一个表调用randomNums了值10,20,30,40,50.
我想生成一个除上述值之外的数字.
我尝试了以下查询.
询问
;WITH CTE AS
(
SELECT FLOOR(RAND()*100) AS rn
)
SELECT rn FROM CTE
WHERE rn NOT IN (SELECT num FROM randomNums);
Run Code Online (Sandbox Code Playgroud)
但有时这个查询什么也不返回.
因为那个时候它会生成表格中的数字randomNums.
如何解决这个问题?
Har*_* CO 11
还有一个选择,我一直喜欢NEWID()随机排序,而且交叉连接可以非常有效地创建很多行:
;with cte AS (SELECT 1 n UNION ALL SELECT 1)
,cte2 AS (SELECT TOP 100 ROW_NUMBER() OVER(ORDER BY a.n) n
FROM cte a,cte b,cte c,cte d, cte e, cte f, cte g)
SELECT TOP 1 n
FROM cte2 a
WHERE NOT EXISTS (SELECT 1
FROM randomNums b
WHERE a.n = b.num)
ORDER BY NEWID()
Run Code Online (Sandbox Code Playgroud)
演示:SQL小提琴
如果您不想使用WHILE循环,那么您可以考虑使用递归的解决方案CTE:
;WITH CTE AS
(
SELECT FLOOR(RAND()*100) AS rn
UNION ALL
SELECT s.rn
FROM (
SELECT rn
FROM CTE
WHERE rn NOT IN (SELECT num FROM randomNums)
) t
CROSS JOIN (SELECT FLOOR(RAND()*100) AS rn) AS s
WHERE t.rn IS NULL
)
SELECT rn
FROM CTE
Run Code Online (Sandbox Code Playgroud)
编辑:
正如下面的评论中所述,上面的方法不起作用:如果第一个生成的数字(来自锚成员)是已经存在于 中的CTE数字,则递归成员的 将返回,因此将返回来自锚成员的数字。randomNumsCROSS JOINNULL
这是一个不同的版本,基于使用递归的相同想法CTE,可以工作:
DECLARE @maxAttempts INT = 100
;WITH CTE AS
(
SELECT FLOOR(RAND()*100) AS rn,
1 AS i
UNION ALL
SELECT FLOOR(RAND(CHECKSUM(NEWID()))*100) AS rn, i = i + 1
FROM CTE AS c
INNER JOIN randomNums AS r ON c.rn = r.num
WHERE (i = i) AND (i < @maxAttempts)
)
SELECT TOP 1 rn
FROM CTE
ORDER BY i DESC
Run Code Online (Sandbox Code Playgroud)
这里,锚定成员首先CTE生成随机数。如果该数字已经存在于递归成员中randomNums,INNER JOIN则递归成员将成功,因此将生成另一个随机数。否则,INNER JOIN将失败并且递归将终止。
还有几点需要注意:
i变量用于记录生成“唯一”随机数的尝试次数。i用于INNER JOIN递归成员的运算,以便仅与前一个递归的随机值相连接。RAND()会返回相同的结果,因此我们必须使用CHECKSUM(NEWID())的种子RAND()。@maxAttempts可以选择用于指定生成“唯一”随机数的最大尝试次数。