生成 sql 服务器中的 table 中不存在的随机数
Generate a random number which is not there in a table in sql server
我正在寻找生成一个随机数,生成的数字在另一个 table 上不存在。
例如:如果一个名为 randomNums
的 table 具有值 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);
但有时这个查询returns什么也没有。
因为那个时候它会生成 table randomNums
.
中的数字
如何解决这个问题?
尝试
declare @n as int
while @n is null and (select COUNT(*) from randomNums) < 100
Begin
;WITH CTE AS
(
SELECT FLOOR(RAND()*100) AS rn
)
SELECT @n = rn FROM CTE
WHERE rn NOT IN (SELECT num FROM randomNums);
End
select @n
只有在排除的数量相对较少的情况下,才建议使用这种方法。
如果您不想使用 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
编辑:
如下面的评论所述,以上内容不起作用:如果第一个生成的号码(来自 CTE
锚定成员)是 已经存在 的号码 randomNums
,则递归成员的 CROSS JOIN
将 return NULL
,因此锚成员的数字将 returned.
这是一个不同的版本,基于使用递归 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
这里,CTE
的主播成员首先生成一个随机数。如果此数字已经存在于 randomNums
中,则递归成员的 INNER JOIN
将成功,因此将生成另一个随机数。否则,INNER JOIN
将失败并且递归将终止。
还有几点需要注意:
i
变量用于记录尝试生成'unique'随机数的次数。
i
的值被用在递归成员的INNER JOIN
操作中,以便与前一个递归的随机值相结合仅.
- 由于使用相同的种子值 return 重复调用
RAND()
得到相同的结果,我们必须使用 CHECKSUM(NEWID())
作为 RAND()
的种子。
@maxAttempts
可以选择性地用于指定为生成 'unique' 随机数而进行的最大尝试次数。
查询
declare @RandomNums table (Num int);
insert into @RandomNums values (10),(20),(30),(40),(50),(60),(70),(80),(90);
-- Make a table of AvailableNumbers
with N as
(
select n from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) t(n)
),
AvailableNumbers as
(
select -- top 97 -- limit as you need
row_number() over(order by (select 1)) as Number
from
N n1, N n2 --, N n3, N n4, N n5, N n6 -- multiply as you need
),
-- Find which of AvailableNumbers is Vacant
VacantNumbers as
(
select
OrdinalNumber = row_number() over(order by an.Number) ,
an.Number
from
AvailableNumbers an
left join @RandomNums rn on rn.Num = an.number
where
rn.Num is null
)
-- select rundom VacantNumber by its OrdinalNumber in VacantNumbers
select
Number
from
VacantNumbers
where
OrdinalNumber = floor(rand()*(select count(*) from VacantNumbers) + 1);
还有一个选择,我一直喜欢 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()
演示:SQL Fiddle
另一种选择是为 table randomNums 创建唯一索引。然后在您的代码中捕获如果生成重复密钥可能出现的错误,在这种情况下选择另一个数字并重试。
我正在寻找生成一个随机数,生成的数字在另一个 table 上不存在。
例如:如果一个名为 randomNums
的 table 具有值 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);
但有时这个查询returns什么也没有。
因为那个时候它会生成 table randomNums
.
如何解决这个问题?
尝试
declare @n as int
while @n is null and (select COUNT(*) from randomNums) < 100
Begin
;WITH CTE AS
(
SELECT FLOOR(RAND()*100) AS rn
)
SELECT @n = rn FROM CTE
WHERE rn NOT IN (SELECT num FROM randomNums);
End
select @n
只有在排除的数量相对较少的情况下,才建议使用这种方法。
如果您不想使用 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
编辑:
如下面的评论所述,以上内容不起作用:如果第一个生成的号码(来自 CTE
锚定成员)是 已经存在 的号码 randomNums
,则递归成员的 CROSS JOIN
将 return NULL
,因此锚成员的数字将 returned.
这是一个不同的版本,基于使用递归 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
这里,CTE
的主播成员首先生成一个随机数。如果此数字已经存在于 randomNums
中,则递归成员的 INNER JOIN
将成功,因此将生成另一个随机数。否则,INNER JOIN
将失败并且递归将终止。
还有几点需要注意:
i
变量用于记录尝试生成'unique'随机数的次数。i
的值被用在递归成员的INNER JOIN
操作中,以便与前一个递归的随机值相结合仅.- 由于使用相同的种子值 return 重复调用
RAND()
得到相同的结果,我们必须使用CHECKSUM(NEWID())
作为RAND()
的种子。 @maxAttempts
可以选择性地用于指定为生成 'unique' 随机数而进行的最大尝试次数。
查询
declare @RandomNums table (Num int);
insert into @RandomNums values (10),(20),(30),(40),(50),(60),(70),(80),(90);
-- Make a table of AvailableNumbers
with N as
(
select n from (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) t(n)
),
AvailableNumbers as
(
select -- top 97 -- limit as you need
row_number() over(order by (select 1)) as Number
from
N n1, N n2 --, N n3, N n4, N n5, N n6 -- multiply as you need
),
-- Find which of AvailableNumbers is Vacant
VacantNumbers as
(
select
OrdinalNumber = row_number() over(order by an.Number) ,
an.Number
from
AvailableNumbers an
left join @RandomNums rn on rn.Num = an.number
where
rn.Num is null
)
-- select rundom VacantNumber by its OrdinalNumber in VacantNumbers
select
Number
from
VacantNumbers
where
OrdinalNumber = floor(rand()*(select count(*) from VacantNumbers) + 1);
还有一个选择,我一直喜欢 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()
演示:SQL Fiddle
另一种选择是为 table randomNums 创建唯一索引。然后在您的代码中捕获如果生成重复密钥可能出现的错误,在这种情况下选择另一个数字并重试。