左连接未被优化

Left join not being optimized

在 SQL 服务器数据库上,考虑如下经典 parent-child 关系:

create table Parent(
p_id uniqueidentifier primary key,
p_col1 int,
p_col2 int
);

create table Child(
c_id uniqueidentifier primary key,
c_p uniqueidentifier foreign key references Parent(p_id)
);

declare @Id int
set @Id = 1
while @Id <= 10000
begin
insert into Parent(p_id, p_col1, p_col2) values (NEWID(), @Id, @Id);
set @Id=@Id+1;
end
insert into Child(c_id, c_p) select NEWID(), p_id from Parent;
insert into Child(c_id, c_p) select NEWID(), p_id from Parent;
insert into Child(c_id, c_p) select NEWID(), p_id from Parent;
;

现在我有这两个等效查询,一个使用内部连接,另一个使用左连接:

内部查询:

select *
from Child c
inner join Parent p
on p.p_id=c.c_p
where p.p_col1=1 or p.p_col2=2;

左连接查询:

select *
from Child c
left join Parent p
on p.p_id=c.c_p
where p.p_col1=1 or p.p_col2=2;

我认为 sql 优化器会足够聪明,可以为这两个查询找出相同的执行计划,但事实并非如此。 内部查询的计划是这样的:

左连接查询的计划是这样的:

优化器工作得很好,选择相同的计划,如果我只有一个条件,如:

where p.p_col1=1 

但是,如果我在第二个不同的列中添加 "or",那么它就不会再选择最佳计划了:

where p.p_col1=1 or p.p_col2=2;

我是不是遗漏了什么,或者只是优化器遗漏了这个改进?

很明显,是优化器。

当您在 WHERE 子句中有一个条件(并且 "condition" 可能是与 AND 相关联的条件,但不是 ORs),则优化器可以很容易地达到峰值并说 "yes, the condition has rows from the second table, there is no NULL value comparison, so this is really an inner join".

当条件由 OR 连接时,逻辑会变得更难。我想您已经观察到优化器不会针对更复杂的条件执行此操作。

有时,如果您更改条件的顺序,生成的计划会有所不同。优化器不会检查所有可能的实现方案(不幸的是)。这就是为什么有时您必须使用提示进行优化的原因。