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)
我的猜测是,因为我有一个聚合,服务器无论如何都必须处理所有行,因此影响不是那么大。
从 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。注意排序和散列操作。
任意选择 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影响执行计划?