为什么更改声明的连接列顺序会引入排序?


40

我有两个表,它们具有相同的命名,类型和索引键列。其中一个具有唯一的聚集索引,另一个具有非唯一索引。

测试设置

设置脚本,包括一些实际的统计信息:

DROP TABLE IF EXISTS #left;
DROP TABLE IF EXISTS #right;

CREATE TABLE #left (
    a       char(4) NOT NULL,
    b       char(2) NOT NULL,
    c       varchar(13) NOT NULL,
    d       bit NOT NULL,
    e       char(4) NOT NULL,
    f       char(25) NULL,
    g       char(25) NOT NULL,
    h       char(25) NULL
    --- and a few other columns
);

CREATE UNIQUE CLUSTERED INDEX IX ON #left (a, b, c, d, e, f, g, h)

UPDATE STATISTICS #left WITH ROWCOUNT=63800000, PAGECOUNT=186000;

CREATE TABLE #right (
    a       char(4) NOT NULL,
    b       char(2) NOT NULL,
    c       varchar(13) NOT NULL,
    d       bit NOT NULL,
    e       char(4) NOT NULL,
    f       char(25) NULL,
    g       char(25) NOT NULL,
    h       char(25) NULL
    --- and a few other columns
);

CREATE CLUSTERED INDEX IX ON #right (a, b, c, d, e, f, g, h)

UPDATE STATISTICS #right WITH ROWCOUNT=55700000, PAGECOUNT=128000;

再现

当我将这两个表连接到它们的集群键时,我期望一对多的MERGE连接,如下所示:

SELECT *
FROM #left AS l
LEFT JOIN #right AS r ON
    l.a=r.a AND
    l.b=r.b AND
    l.c=r.c AND
    l.d=r.d AND
    l.e=r.e AND
    l.f=r.f AND
    l.g=r.g AND
    l.h=r.h
WHERE l.a='2018';

这是我想要的查询计划:

这就是我要的。

(不要担心警告,它们与虚假统计信息有关。)

但是,如果我更改联接中各列的顺序,如下所示:

SELECT *
FROM #left AS l
LEFT JOIN #right AS r ON
    l.c=r.c AND     -- used to be third
    l.a=r.a AND     -- used to be first
    l.b=r.b AND     -- used to be second
    l.d=r.d AND
    l.e=r.e AND
    l.f=r.f AND
    l.g=r.g AND
    l.h=r.h
WHERE l.a='2018';

... 有时候是这样的:

更改联接中声明的列顺序后的查询计划。

Sort运算符似乎根据声明的连接顺序对流进行排序,即c, a, b, d, e, f, g, h,这为我的查询计划添加了阻塞操作。

我看过的东西

  • 我尝试将列更改为NOT NULL,结果相同。
  • 原始表格是使用创建的ANSI_PADDING OFF,但是使用创建的表格ANSI_PADDING ON不会影响该计划。
  • 我尝试了INNER JOIN而不是LEFT JOIN,没有任何变化。
  • 我在2014 SP2 Enterprise上发现了它,并在2017 Developer(现为CU)上创建了副本。
  • 删除前导索引列上的WHERE子句确实会产生良好的计划,但是会影响结果。.::)

最后,我们要解决的问题

  • 这是故意的吗?
  • 我可以在不更改查询的情况下消除排序吗(这是供应商代码,所以我宁愿不要...)。我可以更改表和索引。

Answers:


28

这是故意的吗?

是设计使然。不幸的是,当Microsoft退出Connect反馈站点时,此断言的最佳公开来源丢失了,这消除了SQL Server团队的开发人员的许多有用评论。

无论如何,当前的优化器设计并没有积极地寻求避免本身不必要的排序。这在开窗函数等中最经常遇到,但是在对排序特别是对排序符之间保留的排序敏感的其他运算符中也可以看到。

尽管如此,优化程序在避免不必要的排序方面非常出色(在许多情况下),但是这种结果通常是出于积极尝试使用不同排序组合的原因而发生的。从这个意义上讲,与其说是“搜索空间”,还不如说是正交优化器功能之间复杂的交互作用,它已经被证明可以以可接受的成本提高总体计划质量。

例如,通常可以简单地通过将排序要求(例如,顶层ORDER BY)与现有索引进行匹配来避免排序。在您的情况中,ORDER BY l.a, l.b, l.c, l.d, l.e, l.f, l.g, l.h;这很简单,可能意味着添加,但是这过于简化了(并且不可接受,因为您不想更改查询)。

更一般地,每个备忘录组可以与所需或期望的属性相关联,其可以包括输入顺序。如果没有明显的理由强制执行特定的命令(例如,满足ORDER BY或确保对顺序敏感的物理操作员的结果正确),则涉及“运气”要素。我在避免合并合并串联中的排序中写了更多关于合并联接(在联合或联接模式下)的细节。其中大部分超出了产品支持的表面积,因此应将其视为信息性的,并且可能会发生变化。

是的,在您的特定情况下,您可以按照jadarnel27的建议调整索引以避免出现这种排序。尽管没有什么理由真正喜欢此处的合并联接。您还可以OPTION(HASH JOIN, LOOP JOIN)根据您对数据的了解以及最佳,最差和平均情况下的性能之间的折衷,在不更改查询的情况下使用计划指南在哈希或循环物理联接之间进行选择。

最后,出于好奇,请注意,可以使用简单的避免排序ORDER BY l.b,但可能会降低效率,效率低下的多对多合并联接可能会b单独使用,并且残留复杂的信息。我主要是为了说明我前面提到的优化器功能与顶级需求可以传播的方式之间的相互作用而进行说明。


19

我可以在不更改查询的情况下消除排序吗(这是供应商代码,所以我宁愿不要...)。我可以更改表和索引。

如果可以更改索引,则更改索引​​的顺序#right以匹配联接中过滤器的顺序将删除排序(对我而言):

CREATE CLUSTERED INDEX IX ON #right (c, a, b, d, e, f, g, h)

令人惊讶的是(至少对我而言),这不会导致两个查询都以某种排序结束。

这是故意的吗?

查看一些奇怪的跟踪标志的输出,最终的Memo结构有一个有趣的区别:

每个查询的最终备忘录结构的屏幕截图

正如您在顶部的“根组”中所看到的,两个查询都可以选择使用合并联接作为执行该查询的主要物理操作。

好查询

联接而不排序按组驱动29选项1和组31选项1(其中的每一个都是所涉及的索引范围扫描)。它由组27(未显示)过滤,该组是过滤联接的一系列逻辑比较操作。

查询错误

具有排序的那个由这两个组(29和31)中的每一个具有的(新)选项3驱动。选项3对前面提到的范围扫描的结果执行物理排序(每个组的选项1)。

为什么?

出于某种原因,优化器在第二个查询中甚至无法使用将29.1和31.1直接用作合并联接的源的选项。否则,我认为它将在其他选项中的根目录下列出。如果完全可用,那么它将肯定会从昂贵得多的分拣操作中选择那些。

我只能得出以下结论:

  • 这是优化程序搜索算法中的错误(或更可能是限制)
    • 将索引和联接更改为仅具有5个键将删除第二个查询的排序(6、7和8个键均具有排序)。
    • 这意味着具有8个键的搜索空间太大,以至于优化程序没有时间以“找到足够好的计划”而提前终止该非排序解决方案,因为它没有可行的选择。
    • 在我看来,连接条件的顺序对优化器的搜索过程有很大影响,但这确实有点麻烦,但这确实让我有点头疼
  • 需要排序以确保结果正确
    • 这似乎不太可能,因为当键较少或键以不同顺序指定时,查询可以不进行排序而运行

希望有人可以解释为什么需要这种排序,但是我认为Memo大楼中的差异很有趣,可以作为答案。


1
我相信您对搜索空间的评论实际上就是这种情况。为了仅使用索引,优化器必须验证它们是否满足条件,过去的5个键在必须回退之前有太多的检查可能性。我很好奇,如果查询的所有顺序组合都被枚举,那么有多少优化程序将在vs上成功回落
Mr.Mindor

是的,不一致性确实看起来有点小问题,但是它可能完全取决于用于验证索引是否足够的算法。如果对所有组合进行了测试,您可能可以看到结果中的模式并确定使用哪种算法。我敢打赌它被编写为在更典型的用例中表现最佳。可能存在一种替代方法,它能够在时限内可靠地找到8键解决方案,但是当少于3-4个键时,它比当前解决方案要慢。
Mindor先生
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.