AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / dba / 问题 / 215078
Accepted
Daniel Hutmacher
Daniel Hutmacher
Asked: 2018-08-17 03:58:09 +0800 CST2018-08-17 03:58:09 +0800 CST 2018-08-17 03:58:09 +0800 CST

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

  • 772

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

测试设置

设置脚本,包括一些真实的统计数据:

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 子句确实会产生好的计划,但它会影响结果.. :)

最后,我们进入问题

  • 这是故意的吗?
  • 我可以在不更改查询的情况下消除排序吗(这是供应商代码,所以我真的不想......)。我可以更改表和索引。
sql-server sql-server-2014
  • 2 2 个回答
  • 2215 Views

2 个回答

  • Voted
  1. Best Answer
    Paul White
    2018-08-17T13:58:15+08:002018-08-17T13:58:15+08:00

    这是故意的吗?

    这是设计使然,是的。不幸的是,当 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,或确保来自对命令敏感的物理运算符的正确结果),则涉及“运气”元素。我写了更多关于它的细节,因为它与合并连接(在联合或连接模式下)有关,在使用 Merge Join Concatenation 避免排序。其中大部分超出了产品支持的表面积,因此请将其视为信息,并且可能会发生变化。

    在您的特定情况下,是的,您可以按照 jadarnel27的建议调整索引以避免排序;尽管没有什么理由更喜欢这里的合并连接。您还可以在不更改查询的情况下使用计划指南提示在散列或循环物理连接之间进行选择OPTION(HASH JOIN, LOOP JOIN),具体取决于您对数据的了解,以及最佳、最差和平均情况性能之间的权衡。

    最后,出于好奇,请注意,可以使用简单的 来避免排序ORDER BY l.b,但代价是可能效率较低的多对多合并连接b,并且具有复杂的残差。我提到这主要是为了说明我之前提到的优化器功能之间的交互,以及顶级需求可以传播的方式。

    • 31
  2. Josh Darnell
    2018-08-17T07:08:46+08:002018-08-17T07:08:46+08:00

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

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

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

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

    这是故意的吗?

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

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

    正如您在顶部的“根组”中看到的那样,两个查询都可以选择使用 Merge Join 作为主要物理操作来执行此查询。

    很好的查询

    没有排序的连接由组 29 选项 1 和组 31 选项 1 驱动(每个选项都是对所涉及索引的范围扫描)。它由组 27(未显示)过滤,这是过滤连接的一系列逻辑比较操作。

    错误查询

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

    为什么?

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

    我只能得出以下结论:

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

    希望有人能过来解释为什么需要排序,但我认为备忘录建筑的差异很有趣,可以作为答案发布。

    • 20

相关问题

  • SQL Server - 使用聚集索引时如何存储数据页

  • 我需要为每种类型的查询使用单独的索引,还是一个多列索引可以工作?

  • 什么时候应该使用唯一约束而不是唯一索引?

  • 死锁的主要原因是什么,可以预防吗?

  • 如何确定是否需要或需要索引

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目

    • 12 个回答
  • Marko Smith

    如何让sqlplus的输出出现在一行中?

    • 3 个回答
  • Marko Smith

    选择具有最大日期或最晚日期的日期

    • 3 个回答
  • Marko Smith

    如何列出 PostgreSQL 中的所有模式?

    • 4 个回答
  • Marko Smith

    列出指定表的所有列

    • 5 个回答
  • Marko Smith

    如何在不修改我自己的 tnsnames.ora 的情况下使用 sqlplus 连接到位于另一台主机上的 Oracle 数据库

    • 4 个回答
  • Marko Smith

    你如何mysqldump特定的表?

    • 4 个回答
  • Marko Smith

    使用 psql 列出数据库权限

    • 10 个回答
  • Marko Smith

    如何从 PostgreSQL 中的选择查询中将值插入表中?

    • 4 个回答
  • Marko Smith

    如何使用 psql 列出所有数据库和表?

    • 7 个回答
  • Martin Hope
    Jin 连接到 PostgreSQL 服务器:致命:主机没有 pg_hba.conf 条目 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane 如何列出 PostgreSQL 中的所有模式? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh 为什么事务日志不断增长或空间不足? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland 列出指定表的所有列 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney MySQL 能否合理地对数十亿行执行查询? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx 如何监控大型 .sql 文件的导入进度? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison 你如何mysqldump特定的表? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 对 SQL 查询进行计时? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas 如何从 PostgreSQL 中的选择查询中将值插入表中? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas 如何使用 psql 列出所有数据库和表? 2011-02-18 00:45:49 +0800 CST

热门标签

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve