TOPN 如何通过 group by 影响查询?

kam*_*ahl 3 optimization limits group-by

我有一个如下所示的查询:

Select t1.field1, t2.field2, sum(t1.field3)
From  t1 inner join t2 on t1.id = t2.id
Group by t1.field1, t2.field2
Run Code Online (Sandbox Code Playgroud)

它被简化了,通常上面有更多的连接和属性。我现在的问题是,将 TOP N 子句添加到查询而不是在 N 行返回到我的 C# 代码后停止查询的影响有多大。

我知道,添加该条款对服务器更好,但我对粗略估计该条款有多大影响很感兴趣。我的猜测是,因为我有一个聚合,服务器无论如何都必须处理所有行,因此影响不是那么大。

编辑:C# Snippet 我们如何停止执行

var cmd = new SqlCommand();
cmd.CommandText = sqlStatement;
cmd.Connection = connectionString;
SqlDataReader dr;
dr = cmd.ExecuteReader();
while (dr.HasRows)
{
    while (dr.Read())
    {
        MyDataRow newRow = new MyDataRowDataRow();
        newRow.Parse(dr); //Parse the value from datarow to our format
        result.Add(newRow);
        if (topN.HasValue)
        {
            if (result.Count >= topN.Value)
            {
                break; //stop reading!
            }
        }
    }
    dr.NextResult();
}
Run Code Online (Sandbox Code Playgroud)

Pau*_*ite 9

我的猜测是,因为我有一个聚合,服务器无论如何都必须处理所有行,因此影响不是那么大。

从 SQL Server 的角度来看,这取决于。以下是它所依赖的内容以及原因的概述:

行目标

添加顶级TOP (n)子句(带或不带ORDER BY)具有与指定查询提示相同的行目标效果FAST (n)。行目标使优化器根据快速返回n行来估计计划成本。

因此,小行目标有利于使用非阻塞(流水线)运算符(如(无排序)合并或嵌套循环连接)和半阻塞(每组)运算符(如流聚合)进行串行执行,而不是完全阻塞运算符(如排序和急切散列)清楚的。

如果构建输入预计很小,并且构建输入预计不会重新绑定很多(或在全部)。

流水线执行

在查询逻辑允许的情况下,并且存在合适的索引时,优化器很可能能够找到完全流水线(或者可能是半阻塞)的执行计划。只要组很小,每组阻塞的半阻塞计划可能就可以了。

即使使用最佳索引,在某些情况下,也可能需要使用正确的语法来表达查询,也许还需要使用各种查询和表提示,以获得尽可能流水线化的执行计划。除了最简单的情况外,在所有情况下都很难实现。

流水线/半阻塞计划意味着SqlDataReader.Read()可以以最小的延迟获取第一行和后续行。本质上,C# 代码和 SQL Server 执行引擎都是流式行。在这种情况下,指定TOP (n)会产生最大的不同。

默认策略

如果TOP (n)未指定,查询优化器的目标是查询预期生成的全部潜在总执行时间。例如,优化目标的这种变化倾向于支持并行性、完全阻塞排序和急切散列聚合以及部分阻塞散列连接。这通常会比运行流水线/半阻塞计划(如果可用)到最终结论更快地生成完整集。

当一个或多个阻塞操作符出现在一个计划中时,到第一行的时间可能是潜在整体执行时间的相当大的一部分。这意味着第一个SqlDataReader.Read()调用将阻塞很长时间。在这种情况下,在客户端收到 n 行之后停止 DataReader 对经过的时间的影响相对较小,因为在第一行可用之前,大多数 SQL Server 工作已经完成。

结论

TOP (n)因此,添加的效果至少取决于:

  • 有一个流水线执行计划解决方案(即使在原则上)
  • 合适的访问方法(索引)
  • 查询语法和优化器功能/限制
  • 查询开发人员的技能水平和经验

例子

使用ContosoRetailDW 示例数据集,其中表FactOnlineSales有 12,627,600 行。基于问题中模板的查询是:

SELECT
    FOS.ProductKey, 
    DS.StoreKey, 
    SUM(FOS.SalesAmount)
FROM dbo.DimStore AS DS
JOIN dbo.FactOnlineSales AS FOS
    ON DS.StoreKey = FOS.StoreKey
GROUP BY 
    FOS.ProductKey,
    DS.StoreKey
ORDER BY
    FOS.ProductKey,
    DS.StoreKey;
Run Code Online (Sandbox Code Playgroud)

添加一个有用的索引:

CREATE INDEX i 
ON dbo.FactOnlineSales 
    (ProductKey, StoreKey) 
INCLUDE 
    (SalesAmount);
Run Code Online (Sandbox Code Playgroud)

执行计划是:

默认计划

这运行了1650ms。注意排序和散列操作。

与 TOP (n)

任意选择 n = 50,查询现在是:

SELECT TOP (50)
    FOS.ProductKey, 
    DS.StoreKey, 
    SUM(FOS.SalesAmount)
FROM dbo.DimStore AS DS
JOIN dbo.FactOnlineSales AS FOS
    ON DS.StoreKey = FOS.StoreKey
GROUP BY 
    FOS.ProductKey,
    DS.StoreKey
ORDER BY
    FOS.ProductKey,
    DS.StoreKey;
Run Code Online (Sandbox Code Playgroud)

和执行计划:

流水线计划

这使用流水线迭代器和每组半阻塞流聚合(而不是阻塞急切散列)运行57 毫秒

相关问题:如何(以及为什么)TOP影响执行计划?