我正在写一篇关于排名和聚合窗口函数的博客文章,特别是 Segment 和 Sequence Project 迭代器。我理解它的方式是 Segment 标识流中构成组的结束/开始的行,因此以下查询:
SELECT ROW_NUMBER() OVER (PARTITION BY someGroup ORDER BY someOrder)
将使用 Segment 来判断一行何时属于不同于前一行的不同组。然后,Sequence Project 迭代器根据 Segment 迭代器的输出进行实际的行号计算。
但是使用该逻辑的以下查询不必包含 Segment,因为没有分区表达式。
SELECT ROW_NUMBER() OVER (ORDER BY someGroup, someOrder)
但是,当我尝试这个假设时,这两个查询都使用了 Segment 运算符。唯一不同的是,第二个查询不需要GroupBy
对 Segment 进行 a。这不是首先消除了对 Segment 的需求吗?
例子
CREATE TABLE dbo.someTable (
someGroup int NOT NULL,
someOrder int NOT NULL,
someValue numeric(8, 2) NOT NULL,
PRIMARY KEY CLUSTERED (someGroup, someOrder)
);
--- Query 1:
SELECT ROW_NUMBER() OVER (PARTITION BY someGroup ORDER BY someOrder)
FROM dbo.someTable;
--- Query 2:
SELECT ROW_NUMBER() OVER (ORDER BY someGroup, someOrder)
FROM dbo.someTable;
我发现这篇 6 年前的博文提到了同样的行为。
看起来
ROW_NUMBER()
总是包含一个段运算符,无论是否PARTITION BY
使用。如果我不得不猜测,我会说这是因为它使在引擎上创建查询计划变得更容易。如果在大多数情况下需要该段,并且在不需要它的情况下,它本质上是一个零成本的非操作,那么在使用窗口函数时总是将它包含在计划中要简单得多。
根据执行计划的showplan.xsd
GroupBy
,出现不带minOccurs
ormaxOccurs
属性,因此默认为 [1..1] 使得元素是强制性的,不一定是内容。ColumnReference
( ) 类型的子元素ColumnReferenceType
具有minOccurs
0 和maxOccurs
无界 [0..*],使其成为optional,因此允许为空元素。如果您手动尝试删除GroupBy
并强制执行计划,则会收到预期的错误:有趣的是,我发现您可以手动删除 Segment 运算符以获得有效的强制计划,如下所示:
但是,当您使用该计划(使用
OPTION ( USE PLAN ... )
)运行时,段运算符会神奇地重新出现。只是表明优化器仅将 XML 计划作为粗略的指导。我的试验台:
从测试台中截取 XML 计划并将其保存为 .sqlplan 以查看减去 Segment 的计划。
PS 我不会花太多时间手动处理 SQL 计划,就好像您了解我一样,您会知道我将其视为消耗时间的忙碌工作,而我永远不会这样做。哦等一下!?:)