Sql server 未将谓词推入MSSQL上的左联接

Sql server 未将谓词推入MSSQL上的左联接,sql-server,join,query-optimization,predicate,Sql Server,Join,Query Optimization,Predicate,我试图优化一些复杂的视图,这些视图被简化为一个简单的问题 MSSQL连接两个表,部分在主查询谓词上。问题是,服务器不会对连接的表使用此谓词,直到它实际离开连接状态,结果是从表中读取更多数据,并且查询速度较慢 样本数据 为了说明这个问题,我创建了表示部分视图数据的简单示例: create table A ( ID numeric not null identity, D date not null, ); create table B ( ID numeric not null id

我试图优化一些复杂的视图,这些视图被简化为一个简单的问题

MSSQL连接两个表,部分在主查询谓词上。问题是,服务器不会对连接的表使用此谓词,直到它实际离开连接状态,结果是从表中读取更多数据,并且查询速度较慢

样本数据 为了说明这个问题,我创建了表示部分视图数据的简单示例:

create table A (
  ID numeric not null identity,
  D date not null,
);

create table B (
  ID numeric not null identity,
  A_ID numeric not null,
  DATE_FROM date not null,
  DATE_TO date not null
)

declare @i int = 0
declare @j int
declare @k int
declare @batch int = 1000
declare @a_id int
declare @month date 

begin transaction
while @i < 2000
begin
  set @j = 0
  set @month = dateadd(mm, @i, '1950-01-01')
  while @j < 20
  begin
    insert into a (d) values (@month);
    select @a_id = scope_identity()

    set @k = 0
    while @k < 30
    begin
      insert into b ( a_id, date_from, date_to                                        )
      values        ( @a_id, @month, dateadd(dd, round(rand() * 100, 0), @month) );
      set @k = @k + 1;
      if (@batch = 0)
      begin
        set @batch = 1000
        commit;
        begin transaction
      end
      set @batch = @batch - 1;
    end
    set @j = @j + 1;
  end
  set @i = @i + 1;
end
commit

alter table A add constraint A_PK primary key (ID);
alter table B add constraint B_PK primary key (ID);
alter table B add constraint A_FK foreign key (A_ID) references A(ID);
create index AI on A(D);
create index BI on B(A_ID, DATE_FROM, DATE_TO) include (ID);
结果大约需要80ms,查询计划如下:

基本相同(快速)查询 如果我在左连接中使用右谓词日期:

select A.id
,      B.id
,      B.DATE_FROM
,      B.DATE_TO
from      A
left join B on B.A_ID = A.ID
        and '2000-01-01' between B.DATE_FROM and B.DATE_TO
where A.D = '2000-01-01'
突然间,MSSQL可以实际使用它,并将速度提高到0ms

问题: 如果我删除/更改了索引
IA
IB
或数据量,这两个计划看起来不同,但仍然存在相同的情况:读取联接表时没有谓词,查询速度较慢

问题是为什么MSSQL为这些查询创建不同的计划以及如何在第一个示例中更有效地联接?请注意,我不能使用第二个查询,因为它只是视图的一部分,其中谓词未知

编辑1 关于艾伦的回答,还有一个考验。如果我在谓词中仅使用
ID
DATE\u FROM
,那么优化器也会在谓词上过滤
B

请注意,此更改返回的结果通常不同,但在这里它返回的结果相同(我想在这里并不重要)

编辑2 关于TT的评论(以及Allan的回答),我更改了测试数据以获得更多随机数,因此
A.d
并不总是以
B
开始的间隔。我只将
插入更改为

insert into a (d) values (dateadd(dd, round(rand() * @j, 0), @month));
然后优化器开始按预期工作:

我必须把这作为一个答案,因为它的评论越来越重要:

SQL Server的做法有所不同,因为这两个查询不相同。
对于您来说,它们可能在您的测试示例中是语义上的,但它们不是针对优化器/编译器的。
JOIN子句在WHERE之前处理。这在外部联接中尤其明显,其中ON子句中的参数的含义与where子句中的参数的含义不同

所以在你的第一个例子中,你说-在左边给我全部,在匹配的日期列上和右边做一个外部连接(在没有匹配的地方给我NULL)。最后,它会指出具体的日期。
但是,在第二个示例中,您添加了一个额外的约束,并说在左边给我所有,在右边做一个外部联接,并且日期介于特定日期之间(如果没有匹配项,则给我NULL)。然后在最后处理的位置。
如此微妙,但意义重大不同

您可以很快看到它们是不同的,因为您不必通过规则引擎进行编译和优化,但引擎必须遵循其规则

但是,如果没有关于其他情况的更多信息,我对“优化”的任何建议都可能是不相关的,因为查询的其他部分没有显示

根据这一解释,我认为您甚至必须完全重构您的查询,如果可能的话,还必须重构为多个部分。
然后可以利用一些临时表(而不是表变量),这样就可以开始执行所有内部联接,然后在处理所有内部联接时执行外部联接。 这种方式将允许您过滤掉大量数据,并在外部联接的顶部使用临时结果。

我不知道在你的情况下是否允许你这样做,但考虑到你的“限制”,这个问题可能是“无法解决的”

快速查询更好;您不需要在join子句中引用
A.D
,您已经知道您希望B字段之间有一个特定的日期。@TT。优化器可以做出这样的决定。宣传平等通常是可以做到的。投票选DBA。纯查询优化问题最好在那个网站上解决。你说连接子句是在WHERE之前处理的,但我不确定你说的是物理操作。请看我问题的编辑1。有没有两个查询返回不同结果的情况?现在想不出什么了。
insert into a (d) values (dateadd(dd, round(rand() * @j, 0), @month));