自联接主键


9

考虑以下由N自我联接组成的查询:

select
    t1.*
from [Table] as t1
join [Table] as t2 on
    t1.Id = t2.Id
-- ...
join [Table] as tN on
    t1.Id = tN.Id

它生成具有N个聚集索引扫描和N-1个合并联接的执行计划。

老实说,我看不出没有理由不优化所有联接而仅执行一个聚集索引扫描,即为此优化原始查询:

select
    t1.*
from [Table] as t1

问题

  • 为什么没有优化联接?
  • 说每个联接都不会改变结果集在数学上是不正确的吗?

经过测试:

  • 源服务器版本:SQL Server 2014(12.0.4213)
  • 源数据库引擎版本:Microsoft SQL Server标准版
  • 源数据库引擎类型:独立SQL Server
  • 兼容性级别:SQL Server 2008(100)

查询没有意义;它只是浮现在我的脑海,我对此感到很好奇。

这是创建表和3个查询的小提琴:带有inner join的,带有left join的和混合的。您也可以在那里查看执行计划。

看来left joins在结果执行计划中被消除,而inner joins没有被消除。仍然不知道为什么

Answers:


18

首先,让我们假设它(id)是表的主键。在这种情况下,是的,联接是(可以证明)冗余的,可以消除。

现在仅仅是理论-或数学。为了使优化程序进行实际消除,必须将理论转换为代码并添加到优化程序的优化/重写/消除套件中。为此,(DBMS)开发人员必须认为这将对效率产生很好的好处,并且这是足够普遍的情况。

就个人而言,听起来不像一个(很常见)。就像您承认的那样,该查询看起来很傻,并且除非经过改进并且删除了多余的联接,否则审阅者不应让它通过审阅。

也就是说,确实存在消除的类似查询。Rob Farley有一个很好的相关博客文章:SQL Server中的JOIN简化

在我们的案例中,我们要做的所有工作就是将联接更改为LEFT联接。参见dbfiddle.uk。在这种情况下,优化器知道可以安全地删除连接而不会更改结果。(简化逻辑是很通用的,对于自联接不是特殊情况。)

当然,在原始查询中,删除INNER联接也可能不会更改结果。但是在主键上进行自我联接并不常见,因此优化器没有实现这种情况。但是,在联接列是其中一个表的主键的情况下进行联接(或左联接)是很常见的(并且通常存在外键约束)。这导致消除连接的第二种选择:添加(自我引用!)外键约束:

ALTER TABLE "Table"
    ADD FOREIGN KEY (id) REFERENCES "Table" (id) ;

瞧,连接被淘汰了!(在同一提琴中测试):这里

create table docs
(id int identity primary key,
 doc varchar(64)
) ;
GO
insert
into docs (doc)
values ('Enter one batch per field, don''t use ''GO''')
     , ('Fields grow as you type')
     , ('Use the [+] buttons to add more')
     , ('See examples below for advanced usage')
  ;
GO
受影响的4行
--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    join docs d2 on d2.id=d1.id
    join docs d3 on d3.id=d1.id
    join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | doc                                      
-:| :----------------------------------------
 1 | 每个字段输入一批,请勿使用“ GO”
 2 | 输入时字段会增加                  
 3 | 使用[+]按钮添加更多          
 4 | 有关高级用法,请参见下面的示例    

在此处输入图片说明

--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    left join docs d2 on d2.id=d1.id
    left join docs d3 on d3.id=d1.id
    left join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | doc                                      
-:| :----------------------------------------
 1 | 每个字段输入一批,请勿使用“ GO”
 2 | 输入时字段会增加                  
 3 | 使用[+]按钮添加更多          
 4 | 有关高级用法,请参见下面的示例    

在此处输入图片说明

alter table docs
  add foreign key (id) references docs (id) ;
GO
--------------------------------------------------------------------------------
-- Or use XML to see the visual representation, thanks to Justin Pealing and
-- his library: https://github.com/JustinPealing/html-query-plan
--------------------------------------------------------------------------------
set statistics xml on;
select d1.* from docs d1 
    join docs d2 on d2.id=d1.id
    join docs d3 on d3.id=d1.id
    join docs d4 on d4.id=d1.id;
set statistics xml off;
GO
id | doc                                      
-:| :----------------------------------------
 1 | 每个字段输入一批,请勿使用“ GO”
 2 | 输入时字段会增加                  
 3 | 使用[+]按钮添加更多          
 4 | 有关高级用法,请参见下面的示例    

在此处输入图片说明


2

用关系术语来说,没有属性重命名的任何自连接都是无操作的,可以安全地从执行计划中删除。不幸的是,SQL不是关系型的,优化器可以消除自我联接的情况仅限于少数情况。

SQL的SELECT语法使连接逻辑优先于投影。SQL的列名范围规则以及允许重复的列名和未命名的列的事实使SQL查询优化比关系代数的优化困难得多。SQL DBMS供应商的资源有限,因此必须选择要支持的优化类型。


1

主键始终是唯一的,不允许使用null值,因此在主键上将表连接到自身(不具有自引用辅助键且没有where语句)将产生与原始表相同的行数。

至于为什么他们没有优化它,我会说这是一个极端的情况,他们要么没有计划,要么假设人们不会这样做。在有保证的唯一主键上将表与其自身连接不会达到目的。

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.