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 / 问题 / 165651
Accepted
Alex Friedman
Alex Friedman
Asked: 2017-02-28 09:06:16 +0800 CST2017-02-28 09:06:16 +0800 CST 2017-02-28 09:06:16 +0800 CST

导致扫描的持久化计算列

  • 772

将常规列转换为持久计算列会导致此查询无法执行索引查找。为什么?

在多个 SQL Server 版本上进行了测试,包括 2016 SP1 CU1。

复制品

  • 使用计算列
  • 使用常规列

麻烦在于table1, col7。

表和查询是原件的部分(和简化)版本。我知道可以用不同的方式重写查询,并且出于某种原因可以避免这个问题,但我们需要避免触及代码,为什么table1不能搜索的问题仍然存在。

正如 Paul White 所展示的(谢谢!),如果被强制搜索是可用的,所以问题是:为什么搜索没有被优化器选择,以及我们是否可以做一些不同的事情来使搜索按预期发生,而不改变代码?

为了澄清有问题的部分,这里是错误执行计划中的相关扫描:

计划

sql-server optimization
  • 1 1 个回答
  • 628 Views

1 个回答

  • Voted
  1. Best Answer
    Paul White
    2017-03-01T15:36:22+08:002017-03-01T15:36:22+08:00

    为什么优化器不选择搜索


    TL:DR扩展的计算列定义干扰了优化器最初对连接重新排序的能力。基于不同的起点,基于成本的优化通过优化器采用不同的路径,并以不同的最终计划选择结束。


    细节

    对于除了最简单的查询之外的所有查询,优化器不会尝试探索任何可能计划的整个空间。相反,它会选择一个看起来合理的起点,然后在一个或多个搜索阶段花费预算内的精力探索逻辑和物理变化,直到找到一个合理的计划。

    对于这两种情况,您获得不同计划(具有不同的最终成本估算)的主要原因是起点不同。从不同的地方开始,优化在不同的地方结束(在有限的探索和实施迭代之后)。我希望这是相当直观的。

    我提到的起点在某种程度上是基于查询的文本表示,但是当内部树表示经过查询编译的解析、绑定、规范化和简化阶段时,会对内部树表示进行更改。

    重要的是,确切的起点在很大程度上取决于优化器选择的初始连接顺序。此选择是在加载统计信息之前以及在得出任何基数估计之前做出的。然而,每个表中的总基数(行数)是已知的,已从系统元数据中获得。

    因此,初始连接排序是基于启发式的。例如,优化器尝试重写树,使较小的表在较大的表之前连接,内部连接在外部连接(和交叉连接)之前。

    计算列的存在会干扰此过程,尤其是会干扰优化器将外部连接向下推到查询树中的能力。这是因为计算列在连接重新排序发生之前扩展到它的基础表达式中,并且移动连接通过复杂表达式比移动它通过简单列引用要困难得多。

    涉及的树相当大,但为了说明,非计算列初始查询树以:(注意顶部的两个外部连接)开始

    LogOp_Select
        LogOp_Apply (x_jtLeftOuter) 
            LogOp_LeftOuterJoin
                LogOp_NAryJoin
                    LogOp_LeftAntiSemiJoin
                        LogOp_NAryJoin
                            LogOp_Get TBL:dbo.table1(别名 TBL:a4)
                            LogOp_Select
                                LogOp_Get TBL:dbo.table6(别名 TBL:a3)
                                ScaOp_Comp x_cmpEq
                                    ScaOp_Identifier QCOL:[a3].col18
                                    ScaOp_Const TI(varchar 整理 53256、Var、Trim、ML=16)
                            LogOp_Select
                                LogOp_Get TBL:dbo.table1(别名 TBL:a1)
                                ScaOp_Comp x_cmpEq
                                    ScaOp_Identifier QCOL:[a1].col2
                                    ScaOp_Const TI(varchar 整理 53256、Var、Trim、ML=16)
                            LogOp_Select
                                LogOp_Get TBL:dbo.table5(别名 TBL:a2)
                                ScaOp_Comp x_cmpEq
                                    ScaOp_Identifier QCOL:[a2].col2
                                    ScaOp_Const TI(varchar 整理 53256、Var、Trim、ML=16)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL:[a4].col2
                                ScaOp_Identifier QCOL:[a3].col19
                        LogOp_Select
                            LogOp_Get TBL:dbo.table7(别名 TBL:a7)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL:[a7].col22
                                ScaOp_Const TI(varchar 整理 53256、Var、Trim、ML=16)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL:[a4].col2
                            ScaOp_Identifier QCOL:[a7].col23
                    LogOp_Select
                        LogOp_Get TBL:table1(别名 TBL:cdc)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL:[cdc].col6
                            ScaOp_Const TI(smallint,ML=2) XVAR(smallint,Not Owned,Value=4)
                    LogOp_Get TBL:dbo.table5(别名 TBL:a5)
                    LogOp_Get TBL: table2(别名 TBL: cdt)  
                    ScaOp_Logical x_lopAnd
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL:[a5].col2
                            ScaOp_Identifier QCOL:[cdc].col2
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL:[a4].col2
                            ScaOp_Identifier QCOL:[cdc].col2
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL:[cdt].col1
                            ScaOp_Identifier QCOL:[cdc].col1
                LogOp_Get TBL:table3(别名 TBL:ahcr)
                ScaOp_Comp x_cmpEq
                    ScaOp_Identifier QCOL:[ahcr].col9
                    ScaOp_Identifier QCOL:[cdt].col1
    

    计算列查询的相同片段是:(注意外连接低得多,扩展的计算列定义,以及(内部)连接排序中的一些其他细微差别)

    LogOp_Select
        LogOp_Apply (x_jtLeftOuter)
            LogOp_NAryJoin
                LogOp_LeftAntiSemiJoin
                    LogOp_NAryJoin
                        LogOp_Get TBL:dbo.table1(别名 TBL:a4)
                        LogOp_Select
                            LogOp_Get TBL:dbo.table6(别名 TBL:a3)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL:[a3].col18
                                ScaOp_Const TI(varchar 整理 53256、Var、Trim、ML=16)
                        LogOp_Select
                            LogOp_Get TBL: dbo.table1(别名 TBL: a1
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL:[a1].col2
                                ScaOp_Const TI(varchar 整理 53256、Var、Trim、ML=16)
                        LogOp_Select
                            LogOp_Get TBL:dbo.table5(别名 TBL:a2)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL:[a2].col2
                                ScaOp_Const TI(varchar 整理 53256、Var、Trim、ML=16)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL:[a4].col2
                            ScaOp_Identifier QCOL:[a3].col19
                    LogOp_Select
                        LogOp_Get TBL:dbo.table7(别名 TBL:a7)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL:[a7].col22
                            ScaOp_Const TI(varchar 整理 53256、Var、Trim、ML=16)
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL:[a4].col2
                        ScaOp_Identifier QCOL:[a7].col23
                LogOp_Project
                    LogOp_LeftOuterJoin
                        LogOp_Join
                            LogOp_Select
                                LogOp_Get TBL:table1(别名 TBL:cdc)
                                ScaOp_Comp x_cmpEq
                                    ScaOp_Identifier QCOL:[cdc].col6
                                    ScaOp_Const TI(smallint,ML=2) XVAR(smallint,Not Owned,Value=4)
                            LogOp_Get TBL: table2(别名 TBL: cdt)
                            ScaOp_Comp x_cmpEq
                                ScaOp_Identifier QCOL:[cdc].col1
                                ScaOp_Identifier QCOL:[cdt].col1
                        LogOp_Get TBL:table3(别名 TBL:ahcr)
                        ScaOp_Comp x_cmpEq
                            ScaOp_Identifier QCOL:[ahcr].col9
                            ScaOp_Identifier QCOL:[cdt].col1
                    AncOp_PrjList 
                        AncOp_PrjEl QCOL:[cdc].col7
                            ScaOp_Convert char 整理 53256,Null,Trim,ML=6
                                ScaOp_IIF varchar 整理 53256,Null,Var,Trim,ML=6
                                    ScaOp_Comp x_cmpEq
                                        ScaOp_Intrinsic 是数字
                                            ScaOp_固有权利
                                                ScaOp_Identifier QCOL:[cdc].col4
                                                ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=4)
                                        ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=0)
                                    ScaOp_Const TI(varchar collat​​e 53256,Var,Trim,ML=1) XVAR(varchar,Owned,Value=Len,Data = (0,))
                                    ScaOp_Intrinsic 子串
                                        ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=6)
                                        ScaOp_Const TI(int,ML=4) XVAR(int,Not Owned,Value=1)
                                        ScaOp_Identifier QCOL:[cdc].col4
                LogOp_Get TBL:dbo.table5(别名 TBL:a5)
                ScaOp_Logical x_lopAnd
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL:[a5].col2
                        ScaOp_Identifier QCOL:[cdc].col2
                    ScaOp_Comp x_cmpEq
                        ScaOp_Identifier QCOL:[a4].col2
                        ScaOp_Identifier QCOL:[cdc].col2
    

    在设置初始连接顺序后,加载统计信息并在树上执行初始基数估计。以不同的顺序进行连接也会影响这些估计,因此在以后的基于成本的优化过程中会产生连锁反应。

    最后,对于本节,将外部连接卡在树的中间可以防止在基于成本的优化期间进行一些进一步的连接重新排序规则匹配。


    使用计划指南(或等效的USE PLAN提示 -您的查询示例)将搜索策略更改为更面向目标的方法,由提供的模板的一般形状和特征指导。这解释了为什么在使用计划指南或提示时,优化器可以table1针对计算列架构和非计算列架构找到相同的查找计划。

    我们是否可以做一些不同的事情来让寻求发生

    只有在优化器无法自行找到具有可接受的性能特征的计划时,您才需要担心这一点。

    所有常规的调整工具都可能适用。例如,您可以将查询分解为更简单的部分、审查和改进可用的索引、更新或创建新的统计信息……等等。

    所有这些都会影响基数估计、通过优化器采用的代码路径,并以微妙的方式影响基于成本的决策。

    您可能最终求助于使用提示(或计划指南),但这通常不是理想的解决方案。


    评论中的其他问题

    我同意最好简化查询等,但是有没有办法(跟踪标志)使优化器继续优化并达到相同的结果?

    不,没有用于执行详尽搜索的跟踪标志,而且您不需要。可能的搜索空间很大,超过宇宙年龄的编译时间不会被接受。此外,优化器并不知道每一种可能的逻辑转换(没有人知道)。

    另外,为什么需要复杂的扩展,因为列是持久的?为什么优化器不能避免扩展它,把它当作一个普通的列来对待,并达到相同的起点?

    计算列被扩展(就像视图一样)以提供额外的优化机会。扩展可能会在过程的后面匹配回例如持久化列或索引,但这是在初始连接顺序固定之后发生的。

    • 12

相关问题

  • 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