首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >在MSSQL上没有将谓词推到左联接中

在MSSQL上没有将谓词推到左联接中
EN

Stack Overflow用户
提问于 2016-02-23 11:12:31
回答 1查看 418关注 0票数 1

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

MSSQL将连接两个表,部分位于主查询谓词上。问题是,在连接表实际离开之前,服务器不会使用这个谓词,结果是从表读取更多的数据,查询速度更慢。

样本数据

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

代码语言:javascript
代码运行次数:0
运行
复制
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);

示例(慢速)查询

我试图优化的查询非常简单:

代码语言:javascript
代码运行次数:0
运行
复制
select A.id
,      B.id
,      B.DATE_FROM
,      B.DATE_TO
from      A
left join B on B.A_ID = A.ID and A.D between B.DATE_FROM and B.DATE_TO
where A.D = '2000-01-01'

结果约为80 And ,其查询计划如下:

基本相同(快速)查询

如果我在左侧联接中右边使用谓词日期:

代码语言:javascript
代码运行次数:0
运行
复制
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

问题

如果我删除/更改了索引IAIB或数据量,这两个计划看起来都不一样,但相同的情况仍然存在:没有谓词正在读取已连接的表,查询速度更慢。

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

编辑1

关于艾伦的答案,还有一个考验。如果在谓词中只使用IDDATE_FROM,那么优化器也会在谓词上筛选B

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

编辑2

关于TT的评论(以及Allan的回答),我改变了测试数据,以获得更多的随机性,因此A.d并不总是在B中开始的间隔。我只更改了insert into A

代码语言:javascript
代码运行次数:0
运行
复制
insert into a (d) values (dateadd(dd, round(rand() * @j, 0), @month));

并且优化器开始像预期的那样工作:

EN

回答 1

Stack Overflow用户

发布于 2016-02-23 11:24:32

我不得不把这作为一个答案,因为它正变得越来越重要,需要评论:

因为这两个查询并不相同,所以Server的操作方式不同。

对于您来说,它们在测试示例中可能是语义上的,但它们不是针对优化器/编译器的。

联接子句在WHERE之前处理。这一点在外部联接中尤其明显,其中ON子句中的参数的含义与where子句中的参数的含义不同.

因此,在第一个例子中,您会说--给我左边的所有人,然后在匹配日期列上右侧做一个外部联接(在没有匹配的地方给我NULL )。最后,上面写的是日期的具体位置。

但是,在第二步中,添加了一个额外的约束,并说给我左边的所有,在右边做一个外部连接,日期在特定的日期之间(在没有匹配的地方给我NULL )。然后在最后处理这个地方。

如此微妙但意义重大的不同。

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

但是,如果没有更多关于正在发生的事情的信息,我可以给出的关于“优化”的任何建议都可能无关紧要,因为查询的其他部分没有显示出来。

基于这一解释,我认为您甚至将不得不将查询完全重构,如果可能的话,将其重构为多个部分。

然后您可以利用一些临时表(而不是表变量),因此您可以首先完成所有内部连接,然后在处理所有内部连接时执行外部连接。通过这种方式,您可以将大量数据过滤掉,并在外部联接的顶部使用临时结果。

不管你是否被允许在你的情况下这么做,我不知道--但考虑到你的“约束”,问题可能是“无法解决的”。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/35575985

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档