生成sql server中表中不存在的随机数

生成sql server中表中不存在的随机数,sql,sql-server,sql-server-2008,Sql,Sql Server,Sql Server 2008,我正在寻找生成一个随机数,生成的数字不在另一个表中 例如:如果一个名为randomNums的表的值为10,20,30,40,50 我喜欢在上面的值之外生成一个数字 我尝试了以下查询 质疑 但是有时候这个查询什么也不返回。 因为这一次它生成了表randomNums中的数字 如何解决这个问题 试一试 只有在排除的数量相对较少的情况下,才建议使用这种方法。如果您不想使用WHILE循环,那么您可以研究使用递归CTE的解决方案: 编辑: 如下面的注释中所述,上述操作不起作用:如果CTE锚定成员生成的第一个

我正在寻找生成一个随机数,生成的数字不在另一个表中

例如:如果一个名为randomNums的表的值为10,20,30,40,50

我喜欢在上面的值之外生成一个数字

我尝试了以下查询

质疑

但是有时候这个查询什么也不返回。 因为这一次它生成了表randomNums中的数字

如何解决这个问题

试一试


只有在排除的数量相对较少的情况下,才建议使用这种方法。

如果您不想使用WHILE循环,那么您可以研究使用递归CTE的解决方案:

编辑:

如下面的注释中所述,上述操作不起作用:如果CTE锚定成员生成的第一个数字是randomNums中已经存在的数字,则递归成员的交叉连接将返回NULL,因此锚定成员生成的数字将返回

这是一个不同的版本,基于使用递归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的锚定成员首先生成随机数。如果这个数字已经存在于随机数中,递归成员的内部联接将成功,因此将生成另一个随机数。否则,内部连接将失败,递归将终止

还有几件事需要注意:

i变量用于记录尝试生成“唯一”随机数的次数。 在递归成员的内部联接操作中使用i的值,以便仅与前一个递归的随机值联接。 由于使用相同种子值的重复调用返回相同的结果,因此我们必须使用CHECKSUMNEWID作为RAND的种子。 @maxAttempts可选择性地用于指定最大尝试次数,以生成“唯一”随机数。 质疑


还有一个选项,我一直喜欢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()

演示:

另一个选项是为表randomNums创建一个关于num值的唯一索引。然后,在代码中捕获生成重复密钥时可能出现的错误,在这种情况下,选择另一个数字并重试。

尝试一下,看看是否有。当然,这取决于你的排除清单有多大。在caseIn实践中,您可以始终添加一个limit控件,只有当表包含从1到100的所有数字时,它才应该永远运行,也就是说,它不可能生成有效的数字,并且应该为此添加一个检查。即使如此,每增加一个数字,性能都会下降。如果这是一个问题,另一种方法是从表中的孔中选择,这是@lobodava的答案。哪种方法效果更好取决于你实际拥有的数字数量和范围。你要乘以100。这是否意味着您的范围在0到100之间?我会投票给您唯一有效的解决方案,它不是一个令人讨厌的循环。但是,如果您需要一个介于100万和100万之间的随机数,则此解决方案的工作效率不高,因为它需要每次排序100万个数字。对于大而稀疏的范围,迭代采样方法可以在时间上获胜。没错,在大范围内做到这一点的最快方法是使用一个适当索引的数字表。什么适当的索引可以提供快速随机数?如果必须按NEWID排序,即使是数字表也不会更快。除非你的意思是我们一劳永逸地制定随机序列-我认为这会起作用,只要你小心如何使用随机序列:-是的,你可以构建随机数序列,这取决于使用情况,以上内容非常适合手头的任务。我们不知道他们是在向表中添加随机数,还是出于其他目的不断地选择随机数,并且总是排除子集,如果不向排除表中添加,则预构建的列表将不起作用。下面是一个例子来证明这不起作用。@DavidG确实不起作用。如果第一个生成的数字包含在randomNums中,交叉连接将失败。@DavidG我想我现在已经修复了它!
;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
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
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);
;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()