简体   繁体   English

生成sql server中的表中没有的随机数

[英]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? 如何解决这个问题?

Fiddle for reference 小提琴,供参考

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变量用于记录生成“唯一”随机数的尝试次数。
  • The value of 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操作,以便前一个递归的随机值连接。
  • Since repetitive calls of 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可以选择用于指定为生成“唯一”随机数而进行的最大尝试次数。

SQL Fiddle Demo here SQL小提琴演示在这里

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM