[英]Generate a random number which is not there in a table in sql server
I am looking for generating a random number which the generated number is not there on another table. 我正在寻找生成一个随机数,生成的数字不在另一个表上。
For Example: If a table called randomNums
having the values 10,20,30,40,50
. 例如:如果一个名为
randomNums
的表具有值10,20,30,40,50
。
I like to generate a number apart from the above values. 我想生成一个除上述值之外的数字。
I tried the following query. 我尝试了以下查询。
Query 询问
;WITH CTE AS
(
SELECT FLOOR(RAND()*100) AS rn
)
SELECT rn FROM CTE
WHERE rn NOT IN (SELECT num FROM randomNums);
But sometimes this query returns nothing. 但有时这个查询什么都不返回。
Because that time it generates the number which is there in the table randomNums
. 因为那个时候它会生成表
randomNums
中的randomNums
。
How to solve this issue? 如何解决这个问题?
Yet another option, I've always liked NEWID()
for random ordering, and cross joins create many rows very efficiently: 还有一个选择,我总是喜欢
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()
Demo: SQL Fiddle 演示: SQL小提琴
If you don't want to use a WHILE
loop then you might look into this solution which employs a recursive CTE
: 如果您不想使用
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
EDIT: 编辑:
As stated in comments below the above does not work: If the first generated number (from the CTE
anchor member) is a number already present in randomNums
, then the CROSS JOIN
of the recursive member will return NULL
, hence the number from the anchor member will be returned. 如下面的评论中所述,上述不起作用:如果第一个生成的数字(来自
CTE
锚成员)是randomNums
已存在的randomNums
,则递归成员的CROSS JOIN
将返回NULL
,因此来自锚成员的数字将被退回。
Here is a different version, based on the same idea of using a recursive CTE
, that works: 这是一个不同的版本,基于使用递归
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
Here, the anchor member of the CTE
firstly generates a random number. 这里,
CTE
的锚定成员首先生成随机数。 If this number is already present in randomNums
the INNER JOIN
of the recursive member will succeed, hence yet another random number will be generated. 如果
randomNums
已存在此数字,则递归成员的INNER JOIN
将成功,因此将生成另一个随机数。 Otherwise, the INNER JOIN
will fail and the recursion will terminate. 否则,
INNER JOIN
将失败并且递归将终止。
A couple of things more to note: 还有几点需要注意:
i
variable is used to record the number of attempts made to generate a 'unique' random number. i
变量用于记录生成“唯一”随机数的尝试次数。 i
is used in the INNER JOIN
operation of the recursive member so as to join with the random value of the immediately preceding recursion only . i
的值用于递归成员的INNER JOIN
操作,以便仅与前一个递归的随机值连接。 RAND()
with the same seed value return the same results, we have to use CHECKSUM(NEWID())
as the seed of RAND()
. RAND()
返回相同的结果,因此我们必须使用CHECKSUM(NEWID())
作为RAND()
的种子。 @maxAttempts
can optionally be used to specify the maximum number of attempts made in order to generate a 'unique' random number. @maxAttempts
可以选择用于指定为生成“唯一”随机数而进行的最大尝试次数。 Query 询问
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);
Another option could be to create an unique index on num value for table randomNums. 另一个选项可能是为表randomNums创建num值的唯一索引。 Then in your code catch the possible error if duplicated key is generated, and in that case choose another number and re-try.
然后在您的代码中捕获可能的错误,如果生成了重复的密钥,并在这种情况下选择另一个数字并重试。
Try 尝试
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
It would only be advisable to use this approach, if the number of exclusions is relatively small. 如果排除的数量相对较少,则建议使用此方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.