65

假设我有两个链接的表(一个有一个外键到另一个):

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 等)不同。

4

10 回答 10

49

从理论上讲,不,它不应该更快。查询优化器应该能够生成相同的执行计划。但是,一些数据库引擎可以为其中一个生成更好的执行计划(对于这样一个简单的查询不太可能发生,但对于足够复杂的查询)。您应该测试两者并查看(在您的数据库引擎上)。

于 2009-07-15T07:34:39.370 回答
21

“JOIN”与“WHERE”的性能......一切都取决于数据库引擎能够为您优化查询的能力。它将考虑您在返回的列上可能拥有的任何索引,并考虑 WHERE 和 JOIN 子句的性能也归结为物理数据库文件本身及其碎片级别,甚至是您用于存储数据库文件的存储技术.

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

Microsoft SQL Server 查询处理顺序

以下内容摘自有关 Microsoft SQL Server 的优秀系列书籍,Microsoft SQL Server 2005 内部:T-SQL 查询,可在此处找到

(步骤 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_list 排序

于 2009-07-16T19:15:30.873 回答
13

如果不限于目标数据库,就无法正确回答这个问题。

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

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

真的很冒险,因为很容易忘记 WHERE 子句中的连接条件并最终得到一个讨厌的交叉连接。

于 2009-07-15T07:38:04.263 回答
4

至少在 MySQL 中,它们都将针对相同的查询进行优化。

于 2009-07-15T07:34:33.897 回答
2

使用 INNER JOIN 语法是一种“标准”,尽管实际上是等效的。应该使用它的主要原因是为了清晰和移动性,因为它与 OUTER JOIN 语法一致。

于 2009-07-15T07:41:40.340 回答
2

使用 Sqlite 时: where-syntax 稍微快一些,因为 Sqlite 在执行查询之前首先将 join-syntax 转换为 where-syntax。

于 2009-07-15T07:43:18.417 回答
2

显式连接更易于维护,因为查询的意图更加清晰。此外,它们不会受到意外交叉连接的影响,因此如果您在查询中有交叉连接,维护者知道它应该在那里。

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

于 2009-07-15T13:33:36.283 回答
2

如果您专门谈论 SQL Server,那么您绝对应该使用 INNER JOIN 语法。除了(个人意见警告!)更易于阅读和意图更清晰之外,从 SQL Server 2005 开始,外连接没有等效的语法。2005 年默认不支持 *= 和 =* 语法——您需要启用兼容模式才能支持它。它最终会被删除,可能会在下一个版本发布(或者可能不会!)

这表示:

  • 如果您需要将查询从内部联接更改为外部联接,您需要重写它 (argh) 或启用兼容模式 (yuk)
  • 如果没有兼容模式,您将无法与实现不同类型连接(内部与外部)的方式保持一致,从而导致维护噩梦(并且,如果将两者组合在一个查询中,某些行为是不直观的)。

另请注意,与流行的看法相反,两者并不等同。有些事情要尴尬得多,有些事情根本不可能。Kalen Delaney 的Inside SQL Server 2000涵盖了一些示例;不确定新版本是否可以,因为无论如何都不推荐使用该连接语法。

于 2009-07-16T04:08:08.633 回答
1

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

于 2009-07-15T07:36:15.500 回答
1

我想这也没有什么不同。为了确保您可以检查这两个查询的解释计划是否相同。为了查看 MySQL 中的解释计划,您必须在语句之前放置“解释”关键字,例如:

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

我确信在 MSSQL 中也存在等价物。

顺便说一句:这看起来是 1:1 的关系,所以我只需将 nbviews 属性直接包含在 Document 表中,因此您可以保存连接。

于 2009-07-15T08:04:03.290 回答