2

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

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

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

  • top(@SampleLimit)应用的
  • group by UserId(因为用户 ID 出现多次)
  • order by NEWID()将结果随机排列

SQL:

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

获得此结果集后,我将删除 UserId 是NOT IN上一步中创建的 CTE 的所有结果。

delete QueryResults 
where (GroupId = @GroupId) and (UserId not in(select UserId from cte_temp))

我遇到的问题是,有时我得到的结果多于指定的结果,@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))
4

1 回答 1

5

SELECT所涉及的语句NEWID()将被执行多少次是不确定的。

如果你在 and 之间得到一个嵌套循环反半连接,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() 

然后在DELETE

于 2013-06-14T14:34:40.373 回答