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 / 问题 / 245544
Accepted
i-one
i-one
Asked: 2019-08-17 08:44:15 +0800 CST2019-08-17 08:44:15 +0800 CST 2019-08-17 08:44:15 +0800 CST

恒速扫描加入

  • 772

在准备我之前的Constant Scan 问题时,我VALUES以各种方式进行了试验,并遇到了关于加入的事情,VALUES这对我来说很奇怪。

设置很简单

CREATE TABLE #data ([Id] int);
INSERT INTO #data VALUES (101), (103);

然后有一个查询

DECLARE @id1 int = 101, @id2 int = 102;

SELECT *
FROM (VALUES (@id1), (@id2)) p([Id])
    FULL HASH JOIN #data d ON d.[Id] = p.[Id];

没有什么特别的。如果您运行它,它会工作并产生结果。这是它的执行计划

Constant Scan加盟方案

VALUES但是从中删除一行

SELECT *
FROM (VALUES (@id1)) p([Id])
    FULL HASH JOIN #data d ON d.[Id] = p.[Id];

导致优化器失败

消息 8622,级别 16,状态 1,第 1 行
查询处理器无法生成查询计划...

为什么?有没有办法(除了将参数放入临时表之外)使用哈希算法使其工作?

注意:这不是真正的工具,而是用于研究优化器行为和功能的目的。


上面的例子是在

Microsoft SQL Server 2017 (RTM-CU15-GDR) (KB4505225) - 14.0.3192.2 (X64)

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

1 个回答

  • Voted
  1. Best Answer
    i-one
    2019-08-17T08:44:15+08:002019-08-17T08:44:15+08:00

    为什么?

    简而言之。因为HASH对优化器的腿进行提示射击,而优化器本身对另一条腿进行射击。被射向两个优化器都无法越过终点线。


    为了更好地说明发生了什么,让我们重写有问题的查询以连接两个VALUES并改用合并算法

    DECLARE @id1 int = 101, @id3 int = 103;
    
    SELECT *
    FROM (VALUES (@id1)) p([Id])
        FULL MERGE JOIN (VALUES (@id1), (@id3)) d([Id]) ON d.[Id] = p.[Id];
    

    这个查询的执行计划很简单。有带有两个 Constant Scan 输入的 Merge Join 运算符。

    执行计划

    这两个常量扫描与优化器不同。

    执行计划属性

    表示单行输入的一个列名称的前缀为Expr,而另一个表示多行输入的列名称的前缀为Union。来自多行 Constant Scan 的数据在 Merge Join 谓词中以一种“按引用”( [Union1001]) 的方式访问,而单行 Constant Scan 数据以一种“按值”的方式访问(请参阅@id1被替换而不是[Expr1000])。

    这种“按引用”→“按值”替换是在早期优化阶段执行的标量映射的结果。

    可以看到(使用跟踪标志 8606)在输入树连接谓词中是[Union1001] = [Expr1000]

    *** 输入树:***
        ...
        LogOp_FullOuterJoin
            ...
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier COL:Union1001
                ScaOp_Identifier COL:Expr1000
        ...
    

    但随后在简化树中它变成了[Union1001] = @id1

    *** 简化树: ***
        LogOp_FullOuterJoin
            ...
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier COL:Union1001
                ScaOp_Identifier COL:@id1
    

    标量映射是投影拉动逻辑的一部分,实际上在进入简化阶段之前执行。

    前面可能有人注意到,Merge Join 节点只有剩余谓词,没有连接相等谓词。这是因为连接相等谓词已被标量映射消除。is 相等谓词,[Union1001] = @id1但不能作为连接相等谓词。为此,它必须从两个输入中引用列,但@id1它是可变的而不是列。

    因此,ON d.[Id] = p.[Id]最初是 equijoin,查询转换为非 equijoin(这是一种特殊情况,因此,顺便说一下,优化器没有在 Merge Join 下面为未排序的 Constant Scan 输入引入排序)。幸运的是,在合并算法的情况下,优化器有这样的非等值连接替代方案。

    在使用散列算法的情况下,非等值连接替代方案不存在,因此,连接相等谓词消除会导致优化器稍后失败。


    有没有办法(除了将参数放入临时表之外)使用哈希算法使其工作?

    没有阻止标量映射的跟踪标志(*),无论是查询级别、会话级别还是启动。并且没有可以关闭以防止它的优化规则,因为它不是由规则执行的。

    我只能通过在COptExpr::PexprMapScalar例程中设置断点来执行有问题的查询

    在此处输入图像描述

    eax并在调用后修改寄存器的值,ScaOp_Identifier::ClassNo使 SQL Server 认为第二个操作数ScaOp_Comp不是标识符。

    这是问题中发布的有问题查询的简化树

    *** 简化树: ***
        LogOp_FullOuterJoin
            LogOp_ConstTableGet (1) COL:Expr1000
                ScaOp_Identifier COL:@id1
            LogOp_Get TBL:#data(别名 TBL:d)
            ScaOp_Comp x_cmpEq
                ScaOp_Identifier QCOL:[d].Id
                ScaOp_Identifier COL:Expr1000
    **********************
    

    这是它的计划。

    它实际上没有什么意义,因为获得的计划成本是 0.0210675 个单位,而在没有HASH提示的情况下运行查询会导致使用 M​​erge Join 的执行计划(再次注意 Merge Join 下面没有排序)

    执行计划没有提示

    成本为 0.0088948 个单位。


    (*)可能存在跟踪标志的组合。我认为不是,但我没有探索所有代码路径,所以我不确定。

    • 7

相关问题

  • 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