ON与WHERE的索引表现


26

我有两张桌子

@T1 TABLE
(
    Id INT,
    Date DATETIME
)

@T2 TABLE
(
    Id INT,
    Date DATETIME
)

这些表在(Id,Date)上具有非聚集索引

我加入这些表

SELECT *
FROM T1 AS t1
INNER JOIN T2 AS t2
ON 
    t1.Id = t2.Id
WHERE 
    t1.Date <= GETDATE()
    AND
    t2.Date <= GETDATE()

这也可以写成

SELECT *
FROM T1 AS t1
INNER JOIN T2 AS t2
ON 
    t1.Id = t2.Id
    AND
    t1.Date <= GETDATE()
    AND
    t2.Date <= GETDATE()

我的问题是,这两个查询中哪个查询的性能更好?为什么?还是相等?


1
您是否真的有一个@table变量,该变量具有覆盖所有字段的非聚集索引,而没有聚集索引?还是只是一种简化?
Remus Rusanu

1
这是一个极端的简化
Erik Bergstedt 2015年

Answers:


32

性能将是相同的。优化器将识别出这一点并创建相同的计划。

另一方面,我不会说他们是平等的。第一种形式中的问题是远更具有可读性和普遍预期。

对于使用我手头的一些表的示例,无论我如何编写查询,您都可以看到执行计划完全相同。

您应该能够确定自己的表和数据集的查询计划,以便可以看到您所处情况的情况。

SELECT * FROM salestable , custtable 
WHERE salestable.custaccount = custtable.accountnum 
AND salestable.dataareaid = custtable.dataareaid

SELECT * FROM salestable 
JOIN  custtable 
ON salestable.custaccount = custtable.accountnum 
AND salestable.dataareaid = custtable.dataareaid

SELECT * FROM salestable JOIN custtable 
ON salestable.custaccount = custtable.accountnum 
WHERE salestable.dataareaid = custtable.dataareaid

给出这些执行计划

在此处输入图片说明


我同意,第一种形式更容易阅读,因此,我对它们相等感到放心。我以后只会使用此表格。
Erik Bergstedt,2015年

@ErikBergstedt我编辑了答案,当您查看执行计划时,您应该能够轻松地针对自己的数据集和表结构进行验证
Tom V-Monica团队

是的,我做到了。谢谢。由于未找到答案,我只是在寻求第二意见。
Erik Bergstedt,2015年

注意:如果为,则它们仅相等INNER JOIN。如果您输入一个OUTER JOIN,则它们肯定是不同的。
肯尼斯·费舍尔

22

它们在语义上是相同的,优化器应该毫不费力地认识到这一事实并生成相同的计划。

我倾向于将条件同时引用两个表ON和条件仅引用一个表WHERE

对于OUTER JOINS移动条件但可影响语义。


7

在简单的情况下,它将是相同的。但是,我已经看到带有多个联接的非常复杂的查询具有截然不同的计划。我正在研究的最近一个表开始于一个表,该表将近600万行连接到约20个不同的表。只有该表的第一个联接是内部联接,所有其他联接都是外部联接。where子句中的过滤器已参数化,如下所示:

WHERE table1.begindate >= @startdate AND table1.enddate < @enddate 

该过滤器在计划的后面而不是之前使用。当我将这些条件移到第一个内部联接时,由于在计划的早期应用了过滤器以限制结果集,因此计划发生了巨大变化,并且我的CPU和运行时间减少了大约310%。因此,与许多SQL Server问题一样,这取决于。


2
您能否添加更多详细信息(也许是执行计划图的屏幕截图),因为您的答案似乎与所有其他答案相矛盾?
肯尼·埃维特

2
计划是否显示了最佳超时?
马丁·史密斯

CPU负载如何下降超过100%?
迈克尔·格林

2

通常,放置过滤器的位置会有所不同。
尽管Tom V说优化器会认识到查询是相同的并且提出了相同的计划,但这并不总是正确的。这取决于您所使用的SQL版本,查询的复杂程度以及Optimizer确定查询对整个批处理的重要性。

优化器可能认为这部分批次不值得花足够的时间来制定最佳计划。通常,如果在ON子句而不是WHERE子句中放置减少查询需要处理的数据量的条件,则将获得更好的性能(如果可能,因为使用外部联接执行此操作将导致笛卡尔积) )

偶尔的SQL Developer在WHERE子句中发现过滤器要容易一些,但是我已经处理了一些大型表,在这些表中将过滤器放在ON子句中可以减少运行时间。

因此,如果该子句有可能大大减少查询将读取的行数,我将始终将其放在ON子句中,以帮助Optimizer选择更好的计划。


1

通常情况下,可以在WHERE或JOIN子句中指定过滤条件。我倾向于将过滤器放置在WHERE下,除非可能影响OUTER JOIN优先级(请参阅下文)或该过滤器非常特定于该表(例如TYPE = 12以指定表中特定的行子集)。

另一方面,ON和WHERE子句均可用于指定连接条件(与过滤条件相反)。只要您仅使用INNER联接,在通常情况下使用哪种联接都无关紧要。

但是,如果您使用OUTER联接,则可能会产生很大的不同。例如,如果您在两个表(t1和t2)之间指定了OUTER JOIN,但随后在WHERE子句中继续指定了表之间的eqijoin关系(例如t1.col = t2.col),则只需将OUTER联接转换为INNER联接!这是因为WHERE可以不使用ON子句而指定等值连接(甚至取决于版本,甚至可以使用OUTER连接,使用**语法),并且WHERE指示表之间的内部等值连接时,它将覆盖OUTER加入(如果存在)。

最初的问题是关于过滤器的,其中联接的类型通常不应该成为问题,但是联接也可以充当过滤器,在这种情况下,联接条件的位置当然很重要。


-1

对于INNER JOIN,这是一个样式问题。

但是,使用外部联接变得更加有趣。您应该探索ON子句和WHERE子句中具有OUTER JOIN和条件的查询之间的差异。结果集并不总是相同的。例如

OUTER JOIN dbo.x ON a.ID = x.ID ... WHERE x.SomeField IS NOT NULL

与...相同

INNER JOIN dbo.x ON a.ID = x.ID AND x.SomeField IS NOT NULL

8
如果结果不同(当然是这样),比较性能的重点是什么?
ypercubeᵀᴹ
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.