使用带有CTE的NEWID()来生成行的随机子集会产生奇数结果

Tan*_*ner 2 sql-server stored-procedures newid sql-server-2008-r2

我在存储过程中编写了一些SQL,以将数据集减少到我想要报告的有限随机行数.

报告以a开头Group,Users并应用过滤器来指定所需的随机行总数(@SampleLimit).

为了达到预期的效果,我首先创建一个CTE(临时表):

  • top(@SampleLimit)应用
  • group by UserId (因为UserID出现多次)
  • order by NEWID() 将结果以随机顺序排列

SQL:

; with cte_temp as 
       (select top(@SampleLimit) UserId from QueryResults 
        where (GroupId = @GroupId)
        group by UserId order by NEWID()) 
Run Code Online (Sandbox Code Playgroud)

一旦我有了这个结果集,然后我删除UserId是NOT IN在上一步中创建的CTE的任何结果.

delete QueryResults 
where (GroupId = @GroupId) and (UserId not in(select UserId from cte_temp))
Run Code Online (Sandbox Code Playgroud)

我遇到的问题是,我不时得到的结果比指定的结果多,@SampleLimit而且其他时候完全符合预期.

我已经尝试分解SQL并在应用程序之外执行它,我无法重现该问题.

我正在做的事情有什么根本性的错误可以解释为什么我偶尔会得到更多我要求的结果?

为了完整 - 我的重新考虑的解决方案基于以下答案:

select top(@SampleLimit) UserId into #T1
from  QueryResults
where (GroupId = @GroupId)
group by UserId
order by NEWID() 

delete QueryResults 
where (GroupId = @GroupId) and (UserId not in(select UserId from #T1))
Run Code Online (Sandbox Code Playgroud)

Mar*_*ith 5

判断SELECT涉及的NEWID()执行次数是不确定的.

如果你得到一个嵌套循环反半连接,QueryResults并且cte_temp在计划中没有假脱机,它可能会被重新评估多次,因为QueryResults这意味着对于每个外行,正在与之比较的集合NOT IN可以完全不同.

您可以将结果具体化为临时表,而不是使用CTE来避免这种情况.

INSERT INTO #T
SELECT TOP(@SampleLimit) UserId
FROM   QueryResults
WHERE  ( GroupId = @GroupId )
GROUP  BY UserId
ORDER  BY NEWID() 
Run Code Online (Sandbox Code Playgroud)

然后参考那个 DELETE