连接(JOIN)比条件语句(WHERE)更快吗?

70

假设我有两个相互关联的表(其中一个表具有对另一个表的外键):

CREATE TABLE Document (
  Id INT PRIMARY KEY,
  Name VARCHAR 255
)

CREATE TABLE DocumentStats (
  Id INT PRIMARY KEY,
  DocumentId INT, -- this is a foreign key to table Document
  NbViews INT
)

我知道这不是做事情最聪明的方式,但这是我能想到的最好的例子。

现在,我想获取所有浏览量大于500的文档。 我脑海中出现的两个解决方案是:

SELECT *
FROM Document, DocumentStats
WHERE DocumentStats.Id = Document.Id
  AND DocumentStats.NbViews > 500

或:

SELECT *
FROM Document
INNER JOIN DocumentStats ON Document.Id = DocumentStats.Id
WHERE DocumentStats.NbViews > 500

这两个查询语句是否等效?还是有一种方法比另一种好得多?如果是的话,为什么呢?

编辑:根据答案的要求,此问题旨在针对SQL Server进行,但我也想知道其他数据库引擎(如MySQL等)是否不同。


6
什么数据库?Ms SQL、MySQL、SQLite、Oracle等? - Theo
2
这被称为显式连接与隐式连接,并且已经被问过了:https://dev59.com/j3VD5IYBdhLWcg3wO5AD - Paolo Bergantino
@Theo:主要是MSSQL,但我也对其他的感兴趣...@Paolo:谢谢,我不知道它被称为这样。我尝试使用JOIN vs WHERE进行搜索,但找不到答案。 - Wookai
将下列与编程有关的内容从英语翻译成中文。仅返回翻译后的文本,不要进行任何解释。 - Paolo Bergantino
10个回答

54

理论上,不应该有任何速度提升。查询优化器应该能够生成完全相同的执行计划。然而,某些数据库引擎可以针对其中一个查询语句生成更好的执行计划(对于如此简单的查询来说可能不太可能发生,但对于足够复杂的查询可能会)。您应该在您的数据库引擎上测试两种方式并查看结果。


5
推荐测试 OP 的具体情况并给出 +1。性能分析至关重要。 - Daren Thomas
2
我同意。我经常尝试使用连接和WHERE查询的两个版本,以查看哪个性能更好。有时JOIN更快,有时WHERE更快——这取决于查询。 - Jon Cram

22

"JOIN"和"WHERE"的性能取决于数据库引擎优化查询的能力。它会考虑到返回列上可能存在的任何索引,并且还会考虑WHERE和JOIN子句的性能也取决于物理数据库文件本身及其碎片级别,甚至是您用于存储数据库文件的存储技术。

SQL Server 按以下顺序执行查询(这应该让您了解 WHERE 和 JOIN 子句的功能)

Microsoft SQL Server 查询处理顺序

以下内容摘自有关 Microsoft SQL Server 的优秀书籍系列《Inside Microsoft SQL Server 2005: T-SQL Querying》(可以在这里找到)

(第8步) SELECT (第9步) DISTINCT (第11步) <top_specification> <select_list>
(第1步) FROM left_table
(第3步) join_type JOIN right_table
(第2步) ON join_condition
(第4步) WHERE where_condition
(第5步) GROUP BY group_by_list
(第6步) WITH [CUBE|ROLLUP]
(第7步) HAVING having_clause
(第10步) ORDER BY order_by_list


1
啊,喜欢这个答案!通过试错发现 SQL S. 首先执行 ON,然后是 WHERE,最后是 HAVING。通常不重要,除非涉及 NULLs 的某些情况 - 在 ON 或 WHERE 中具有过滤条件可能会在这些情况下产生很大的差异。现在我想问,这种行为是 ANSI 标准规定还是仅适用于 SQL Server?如果您不确定,我可能会就此提出问题! - Joe Pineda
澄清:我所说的“这种行为”是指先评估ON,然后再评估WHERE的行为。Theo在他的回答中表示,SQLite首先将JOIN...ON转换为WHERE子句。如果这是真的(不知道,现在无法验证),那么先评估ON,然后再评估WHERE可能非常特定于SQL Server(可能也适用于Sybase)。 - Joe Pineda
1
这个查询执行顺序是 Microsoft SQL Server 执行查询的方式。 - Mike J

13

没有办法在不限制目标数据库的情况下正确回答这个问题。

对于 MS-SQL,两个查询都会产生相同的执行计划,但请注意:

SELECT *
FROM Document, DocumentStats
WHERE DocumentStats.Id = Document.Id
  AND DocumentStats.NbViews > 500

使用WHERE子句连接表是非常冒险的,因为很容易忘记连接条件并导致不良的交叉连接。


4

至少在MySQL中,它们都被优化为相同的查询。


2

使用INNER JOIN语法是一种“标准”,虽然实际上是等价的。主要原因是为了清晰和可移植性,因为它与OUTER JOIN语法保持一致。


2
当你使用SqLite时:使用where语法比使用join语法稍微快一些,因为在执行查询之前,SqLite会将join语法转换为where语法。

非常有趣。你能提供一下这种行为的参考资料吗?这意味着在ON子句中放置过滤条件而不是在WHERE子句中放置过滤条件,在SQL Server上可能会产生不同的结果,但在这里却完全相同! - Joe Pineda
请您提供一下参考资料吗? - tiboo
1
SQLite查询规划器,第5.0节连接(http://www.sqlite.org/optoverview.html)。 - David Knight

2

显式连接更易于维护,因为查询的意图更加明确。此外,它们不会受到意外交叉连接的影响,因此如果查询中有交叉连接,维护者知道它是有意为之。

如果您需要使用外连接,您应该知道在SQL Server中,*=语法已经被弃用,并将很快被删除。此外,它目前并不总是按预期工作,可能无法给出正确的结果,因此绝不能使用。混合使用显式外连接和where子句连接(隐式连接)会使查询对维护者更难阅读和理解。


2
如果你特别谈论SQL Server,那么你应该使用INNER JOIN语法。除了更易于阅读和更明确的意图之外(个人意见警告!),从SQL Server 2005开始,没有等价的外连接语法。*=和=*语法在2005年默认情况下不受支持--您需要启用兼容模式才能支持它。它最终将被删除,可能会在下一个版本中尽快删除(或者也可能不会!)
这意味着:
- 如果您需要将查询从内部连接更改为外部连接,则需要重新编写它(啊)或启用兼容模式(yuk) - 没有兼容模式,您无法在实现不同类型的连接(内部与外部)时保持一致,这会导致维护噩梦(并且,在一个查询中组合两者时,会产生一些非直观的行为)。
请注意,与流行观点相反,这两者不是等价的。有些事情更加笨拙,有些事情根本不可能。Kalen Delaney的 Inside SQL Server 2000 涵盖了一些例子;不确定更新的版本是否有,因为该联接语法已被弃用。

1

我猜这应该没有什么区别。为了确认,你可以检查这两个查询的执行计划是否相同。在MySQL中查看执行计划需要在语句前加上"explain"关键字,例如:

EXPLAIN
SELECT *
FROM Document, DocumentStats
WHERE DocumentStats.Id = Document.Id
  AND DocumentStats.NbViews > 500

我相信在MSSQL中也存在等效的方法。

顺便说一下: 这看起来像是一个一对一的关系,所以我会直接将nbviews属性包含在Document表中,这样你就可以节省一个连接。


1
在MSSQL中,这两个查询都编译成相同的执行计划,所以没有区别。更多的是关于可读性 - 我认为JOIN更容易阅读,所以我使用它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接