SQL Group By Count 过滤优化

Posted

技术标签:

【中文标题】SQL Group By Count 过滤优化【英文标题】:SQL Group By Count Filtering Optimization 【发布时间】:2021-03-23 10:40:38 【问题描述】:

我想知道使用过滤器按计数分组是否效果最佳,这意味着我们假设我们有以下查询:

SELECT TenantId FROM SomeTable GROUP BY TenantId HAVING Count(*) >= 2

是否计算每个组的所有元素,然后过滤组,或者将进行优化,以便一旦某些组的元素超过 2 个,剩余元素将不计算在内,并且组将包含在最终结果中?

如果没有优化,假设每个组都有很多行,那么以下查询会更好地工作:

SELECT [t1].TenantId FROM (
  SELECT [t0].TenantId FROM SomeTable AS [t0]
  GROUP BY [t0].TenantId) AS [t1]
WHERE (
  SELECT COUNT(*)
  FROM (
  SELECT TOP(2) NULL AS [EMPTY]
  FROM SomeTable AS [t2]
  WHERE [t1].TenantId = [t2].TenantId
  ) AS [t3]
) >= 2

【问题讨论】:

为什么不运行 2 个查询并比较查询计划和执行时间?我们无权访问您的实例,也不知道表的定义、索引、数据的大小、值的分布或其他任何事情。我怀疑前者会快得多,因为它会导致 1 次访问表 SomeTable,前提是您有正确的索引,但 you 比测试这个位置要好得多我们是。 最初的问题是 group by 在这种情况下是否计算每个组的所有行,或者一旦在其中找到超过 2 个元素就对其进行过滤,我如何在执行计划中检查?跨度> 第一个问题是“我想知道使用过滤器按计数分组是否效果最佳,”如果查询考虑了所有行,则不是。 我在哪里说是你? :) 字面意思在评论中引用了它... “只需要在投票前阅读整个问题” 没有其他人与之交流你在这些cmets中。您的评论是对我自己的回应,所以这句话显然是针对我自己的。根据设计,上下投票是匿名的。您知道某人是否对某个问题投反对票的唯一方法是他们是否明确声明他们有,或者您有权访问 Stack Overflow 数据库。如果评论不是针对我的,那很好,但我建议你澄清一下,因为它不是这样读的。谢谢。 【参考方案1】:

为了能够按照您的要求进行操作,您需要跳过扫描。 换句话说,服务器需要从每个组中读取必要的行数,然后跳过并查找下一组。

不幸的是,SQL Server 没有实现跳过扫描。这部分是因为它实际上并没有你想象的那么有用,因为不断地跳过索引通常比一开始就扫描整个索引效率低。


正如您所说,它可能取决于每组中的行数。如果且仅当组的大小相对于HAVING COUNT(*) 子句非常大,那么可能值得自己实施跳过扫描,以便获取不同的值,as show by Paul White in this great article。使用递归 CTE 可以很困难地完成,但我不确定如果您也在分组,它将如何优化。

所以我将向您展示迭代表变量解决方案,它应该同样快。这项技术的所有功劳归功于 Paul White

DECLARE @Results TABLE (TenantId int NOT NULL, cnt int NOT NULL);
DECLARE @next int, @cnt int; 

SELECT TOP (1)
       @tenantId = TenantId, @cnt = COUNT(*)
    FROM SomeTable
    GROUP BY TenantId
    ORDER BY TenantId;

WHILE (@cnt > 0)
BEGIN
    INSERT @Results (TenantId, cnt) VALUES (@TenantId, @cnt);
    
    SELECT TOP (1)
           @tenantId = TenantId, @cnt = COUNT(*)
        FROM SomeTable
        WHERE TenantId > @tenantId
        GROUP BY TenantId
        ORDER BY TenantId;
END;

SELECT TenantId
FROM @Results
WHERE cnt >= 2;

【讨论】:

非常感谢您的详细解释,那么第二个查询在某些情况下会比简单的分组更好吗? 不行,因为它仍然需要对整个表进行扫描和分组,它会更慢而不是更快。虽然唯一确定的方法是计时并检查查询计划【参考方案2】:

数据库可以将过滤移动到聚合计算中。不过,我认为这样的优化并不常见。

我不明白您为什么认为第二个查询会更好。它还聚合整个表——然后使用更复杂的过滤子句。很难想象它会更快,但您可以随时检查。

可能更快的是过滤before聚合。如果您有大型组、主键和正确的索引:

SELECT TenantId
FROM SomeTable st
WHERE EXISTS (SELECT 1
              FROM SomeTable st2
              WHERE st2.TenantId = st.TenantId AND
                    st2.primary_key <> st.primary_key
             )
GROUP BY TenantId;

【讨论】:

以上是关于SQL Group By Count 过滤优化的主要内容,如果未能解决你的问题,请参考以下文章

Mysql count group_concat 高级用法(count 过滤条件,group_concat过滤条件)

sql select group by a count(1) > 1 在python pandas中等效?

mysql group by 分组及having 过滤分组

MySQL-SQL优化:主键,order by,group by,limit,count,update

Codeigniter 中由 distinct() 或 group_by() 过滤的计数结果

sql语句中的group by啥意思