内连接 vs Where条件

303

在 Oracle 中,使用 SELECT * 和明确指定所需列的性能是否有差异?

Select * from Table1 T1 
Inner Join Table2 T2 On T1.ID = T2.ID

并且

Select * from Table1 T1, Table2 T2 
Where T1.ID = T2.ID

?


2
仅供记录,我曾经看到查询只是通过从连接改为where子句返回不同的结果。 - BlackTigerX
13
不同的结果?是否涉及到外连接?因为我不明白在使用"inner join...on"和在"where"子句中放置等效的连接条件之间如何产生不同的结果。 - Shannon Severance
在旧版本的Oracle数据库中不存在join - MajidTaheri
2
@MajidTaheri:你说的是哪个版本的Oracle?任何由Oracle支持的版本都支持JOIN符号。 - Jonathan Leffler
请注意不要只基于琐碎案例给出通用答案或最佳实践。希望看到更复杂的where子句,涉及多个AND条件的“重新匹配”。至少在SQL Server中有一个交叉点。 - crokusek
这里需要注意的一点是,如果你使用了 inner join 但是忘记了加上 on 子句,会导致错误。然而,如果你在 , 分隔的表中忘记了加上 where 子句,它会返回一个交叉连接! - Shivam Tripathi
19个回答

223

不行!同一个执行计划,看看这两个表:

CREATE TABLE table1 (
  id INT,
  name VARCHAR(20)
);

CREATE TABLE table2 (
  id INT,
  name VARCHAR(20)
);

使用内连接查询的执行计划:

-- with inner join

EXPLAIN PLAN FOR
SELECT * FROM table1 t1
INNER JOIN table2 t2 ON t1.id = t2.id;

SELECT *
FROM TABLE (DBMS_XPLAN.DISPLAY);

-- 0 select statement
-- 1 hash join (access("T1"."ID"="T2"."ID"))
-- 2 table access full table1
-- 3 table access full table2

使用WHERE子句查询的执行计划。

-- with where clause

EXPLAIN PLAN FOR
SELECT * FROM table1 t1, table2 t2
WHERE t1.id = t2.id;

SELECT *
FROM TABLE (DBMS_XPLAN.DISPLAY);

-- 0 select statement
-- 1 hash join (access("T1"."ID"="T2"."ID"))
-- 2 table access full table1
-- 3 table access full table2

4
我很想知道Oracle是否有官方文件涉及此事。 - 4 Leave Cover

76

如果查询优化器工作正常,这两个查询之间不应该有任何区别。它们只是表示相同期望结果的两种方式。


25
好的,性能应该是相同的。但是SELECT * FROM Table1, Table2 WHERE...语法是有问题的! - Joel Coehoorn
2
我发现理解FOR INNER JOINS比SQL-92语法要容易得多。你的情况可能会有所不同。 - Craig Trader
33
我认为WHERE语法比INNER JOIN更易于阅读 - 就像Vegemite一样。世界上大多数人可能觉得它很恶心,但是在吃惯了它的孩子们却很喜欢。 - ScottCher
3
维基美味酱确实很难吃,但我却喜欢猪肉糜。真是奇怪。 - StingyJack
2
@Darryl 我认为使用JOIN更易于阅读,因为连接表的条件是直接在那里定义的,而不是在WHERE子句中的“某个地方”定义的。我更喜欢将WHERE子句保留用于限制数据集(例如 WHERE DATE > (SYSDATE - 1)),而不是定义表之间的关系(例如 WHERE T1.ID = T2.ID)。对于像问题中的示例这样的小表,几乎没有区别,但对于涉及多个表和多个条件的大查询,我认为这使查询更容易理解。 - ImaginaryHuman072889
显示剩余3条评论

74

它们应该完全相同。然而,作为一种编码实践,我更倾向于使用 Join。这样可以清晰地表达您的意图。


12
我同意。特别是当您连接多个表时,如果您使用显式连接,则解析SELECT语句会容易得多。 - Paul Morie
17
确实。连接表示两组数据间的语义关系,而 where 表示筛选后的数据集。+1 - EightyOne Unite

33

使用 JOIN 使代码更易于阅读,因为它是自我解释的。

速度上没有差别(我刚刚测试过),执行计划也相同。


1
谢谢。我正在寻找这两种方法之间的速度压缩。 - Farhad Navayazdan

19

[额外加分...]

使用JOIN语法可以更容易地将连接注释掉,因为它们都包含在一行中。如果您正在调试复杂的查询,这可能很有用。

正如其他人所说,它们在功能上是相同的,但JOIN更清晰地表达了意图。因此,在某些情况下,它可能帮助当前Oracle版本中的查询优化器(我不知道是否有用),或者在未来的Oracle版本中可能有帮助(没有人知道),或者当你更改数据库供应商时可能有帮助。


2
或者...将INNER JOIN轻松更改为LEFT JOIN,这样您就可以看到哪个连接导致您错过了预期的行。我之所以这样做,是因为我一次执行整个查询。如果您注释掉INNER JOINS,您需要进行逐步排除的过程。这需要更长的时间。但是+1给您,因为这是我最喜欢使用INNER JOINS的原因之一,除了可读性! - Matthew McPeak

15

我不知道Oracle的情况,但我知道在SQL Server中,旧语法已被弃用并将最终消失。在使用这种旧语法之前,我会先检查一下Oracle计划如何处理它。

我更喜欢新的语法,而不是将连接条件与其他必要的where条件混合在一起。在新的语法中,很清楚哪些内容创建了连接,以及应用了哪些其他条件。在短查询中这并不是一个大问题,但当您有一个更复杂的查询时,情况会变得更加混乱。因为人们从基本查询开始学习,所以我倾向于让人们在需要进行复杂查询之前学习使用连接语法。

再次强调,我不知道Oracle的具体情况,但我知道SQL Server版本的旧式左连接即使在SQL Server 2000中也存在缺陷,并会给出不一致的结果(有时是左连接,有时是交叉连接),因此不应使用。希望Oracle没有遇到同样的问题,但是在旧语法中,左连接和右连接可能更难正确表达。

此外,我的经验是(当然这仅仅是个人意见,你可能有不同的经验),使用ANSII标准连接的开发人员往往更好地理解连接与从数据库中获取数据的含义。我认为这是因为大多数具有良好数据库理解的人倾向于编写更复杂的查询,而使用ANSII标准比旧式连接语法更容易维护。


1
阿门兄弟。反对加入者!! - ScottCher

13

它们在逻辑上是相同的,但在早期采用ANSI语法的Oracle版本中,在更复杂的情况下使用它时常常会遇到错误,因此当你使用它时,有时会遇到来自Oracle开发人员的阻力。


Oracle 的早期版本存在这个 bug 吗?有多早?是哪个版本? - ScottCher
Metalink有详细信息...它们随处可见地弹出。 - David Aldridge

7
性能应该是一样的,但由于外连接更清晰,我建议使用连接版本。
此外,使用连接版本可以避免意外的笛卡尔积。
第三个效果是SQL更易阅读,WHERE条件更简单。

我真的认为关键在于模糊标准下的无意效应。如果您指定连接类型,您就知道自己得到了什么。我发现不同的数据库甚至是同一数据库平台的不同版本在隐式连接中处理空值的方式也不同。当您指定左/右内/外连接时,您会花时间考虑哪个是正确的。当您使用模糊方法时,您假设它按照您所希望/打算的方式工作。 - Steve Kallestad

6
在三范式的情况下,表之间的连接不应该改变。即连接客户和付款应始终保持不变。
但是,我们应该区分连接和过滤器。连接涉及关系,而过滤器涉及将整个分成部分。
一些作者提到了采用JOIN语法而不是FROM子句中的逗号分隔表格的好处。我完全同意这个观点。
有多种编写SQL并实现相同结果的方法,但对于许多团队合作的人来说,源代码的可读性是一个重要方面。将表格相互关联与特定的过滤器分开可以明确地阐明源代码。

内连接(Inner join)的意思是交叉连接(cross join)加条件筛选。逗号表示的交叉连接优先级低于关键字连接。对于内连接,使用“on”和“filter”没有区别。范式对查询无影响。标准中没有推广关键字连接优于逗号连接。微小的优化会将“on”和“where”视为相同。这个回答充斥着误解。 - philipxy
我猜@philipxy的意思是“在FROM子句中,用逗号分隔的表与使用CROSS JOIN连接的表具有相同的含义。” 我同意这个观点。当然,你可以坚持使用旧语法,并且它可以与JOIN子句共存。如果它们是等效的话,关系型数据库管理系统优化器将完全理解两种情况,并制定相同的计划。 - abrittaf

6
不要忘记,在Oracle中,只要连接键属性在两个表中的名称相同,您也可以将其编写为:
select *
from Table1 inner join Table2 using (ID);

这当然也有相同的查询计划。

我回滚了这个版本,因为之前的修订改变了答案的意思。 - juan

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