Oracle的加号(+)和ansi JOIN表示法之间的区别?


Answers:


97

在AFAIK中,该(+)符号仅用于向后兼容,因为Oracle在将ANSI标准用于连接之前将其首次使用。它特定于Oracle,并且当存在等效的符合标准的版本时,应避免在新代码中使用它。

似乎两者之间存在差异,并且该(+)符号具有ANSI连接语法所没有的限制。Oracle自己建议您不要使用该(+)符号。Oracle®数据库SQL语言参考11g第1版(11.1)中的完整描述:

Oracle建议您使用FROM子句OUTER JOIN语法而不是Oracle join运算符。使用Oracle连接运算符的外部连接查询(+)受以下规则和限制的约束,这些规则和限制不适用于FROM子句OUTER JOIN语法:

  • 您不能(+)在还包含FROM子句连接语法的查询块中指定运算符。
  • (+)操作者可以只出现在WHERE(指定时子句,或者在左相关的上下文TABLE中的条款)FROM条款,并且只能被应用到的表或视图的一列。
  • 如果A和B通过多个联接条件联接,则必须使用 (+)在所有这些条件中运算符。如果不这样做,那么Oracle数据库将仅返回由简单联接产生的行,而不会发出警告或错误,以提醒您没有外部联接的结果。
  • (+),如果你在一个内部查询外部查询和其他表指定一个表操作不会产生一个外部联接。
  • (+)尽管自联接有效,但是您不能使用运算符将表外部联接到自身。

例如,以下语句无效:

SELECT employee_id, manager_id
FROM employees
WHERE employees.manager_id(+) = employees.employee_id;

但是,以下自我联接是有效的:

SELECT e1.employee_id, e1.manager_id, e2.employee_id
FROM employees e1, employees e2
WHERE e1.manager_id(+) = e2.employee_id;
  • (+)操作员可以仅应用于一列,而不是任意表达式。但是,任意表达式可以包含一个或多个标有(+)运算符的列。
  • WHERE包含(+)运算符的条件不能使用OR逻辑运算符与另一个条件组合。
  • WHERE条件不能使用IN比较条件,比较标有一列(+)带表达式运算符。

如果该WHERE子句包含将表B中的列与常量进行比较的条件,则(+)必须将运算符应用于该列,以便Oracle返回表A中为其生成此列为空的行。否则,Oracle仅返回简单联接的结果。

在执行多于两对表的外部联接的查询中,单个表可以是仅另一个表的空生成表。因此,您不能将(+)运算符应用于A和B的联接条件以及B和C的联接条件的B列。SELECT有关外部联接的语法,请参见。


docs.oracle.com/cd/B19306_01/server.102/b14200/queries006.htm
My-Name-Is

13

最全面的答案显然是那古尔的答案

对于那些正在寻求快速转换/映射到ANSI语法的人的补充:

--
-- INNER JOIN
--
SELECT *
FROM EMP e
INNER JOIN DEPT d ON d.DEPTNO = e.DEPTNO;

 -- Synonym in deprecated oracle (+) syntax
SELECT *
FROM EMP e,
     DEPT d
WHERE d.DEPTNO = e.DEPTNO;

--
-- LEFT OUTER JOIN
--
SELECT *
FROM EMP e
LEFT JOIN DEPT d ON d.DEPTNO = e.DEPTNO;

 -- Synonym in deprecated oracle (+) syntax
SELECT *
FROM EMP e,
     DEPT d
WHERE d.DEPTNO (+) = e.DEPTNO;

--
-- RIGHT OUTER JOIN
--
SELECT *
FROM EMP e
RIGHT JOIN DEPT d ON d.DEPTNO = e.DEPTNO;

-- Synonym in deprecated oracle (+) syntax
SELECT *
FROM EMP e,
     DEPT d
WHERE d.DEPTNO = e.DEPTNO(+);

--
-- CROSS JOIN
--
SELECT *
FROM EMP e
CROSS JOIN DEPT d;

 -- Synonym in deprecated oracle (+) syntax
SELECT *
FROM EMP e,
     DEPT d;

--
-- FULL JOIN
--
SELECT *
FROM EMP e
FULL JOIN DEPT d ON d.DEPTNO = e.DEPTNO;

-- Synonym in deprecated oracle (+) syntax !NOT WORKING!
SELECT *
FROM EMP e,
     DEPT d
WHERE d.DEPTNO (+) = e.DEPTNO(+);

12

从Oracle 10(我相信11)开始,仍支持该表示法。它的使用被认为是“过时的”,并且不像ANSI JOIN语法那样具有数据库可移植性。尽管如果您来自+背景,但习惯于ANSI JOIN可能会花费一些时间,但是它也被认为不那么可读。在甲骨文公司投奔之前,要了解的重要一点是,他们在ANSI委员会完成联接定义之前就已经开发了+语法。

没有性能差异;他们在表达同一件事。

编辑:通过“不那么可移植”我应该说“仅在Oracle SQL中支持”


13
但是请注意,在使用ANSI语法时,Oracle优化器生成了许多错误,这些错误导致Oracle优化器生成了错误的查询计划或错误的结果。在11.1中,这种情况比在10.1或10.2中要少得多,但是这种情况经常发生,而且已经烧死了很多人,这些人是ANSI语法的早期采用者,因此Oracle社区的大部分人都不愿采用ANSI语法。
贾斯汀·凯夫

6
我有一个示例,您仍然被迫使用旧的Oracle Join语法:如果使用创建一个MATERIALIZED VIEWFAST REFRESH则不能使用ANSI语法。我刚刚在Oracle页面上检查了此问题,它仍然存在。Oracle认为这不是错误!有关详细信息,请参见Oracle Doc ID 1372720.1,以防您可以访问Oracle支持。
Wernfried Domscheit,2014年

1
我只是将a更改LEFT OUTER JOIN(+)语法,查询从1分钟变为600毫秒。相同的准确结果,相同的解释计划。我真的不确定“没有性能差异”部分...我们正在运行11.2.0.3.0
LeoLozes

@LeoLozes,我怀疑您的查询在语义上不等效。例如,以下两个语句是相同的:从d.deptno = e.deptno上的d左外部联接emp e选择d。*,e。*,并按d.deptno选择job ='SALESMAN';并从dept d,emp e中选择d。*,e。*,其中d.deptno(+)= e.deptno,而job ='SALESMAN'由d.deptno排序;
BobC

12

我同意托尼·米勒(Tony Miller)的回答,并想补充一点,您还无法使用(+)合成器做一些事情:

  • 您不能对两个表进行FULL OUTER JOIN,而必须使用两个联接的UNION ALL手动完成,
  • 您不能将一个表外部联接到两个或多个表,而必须手动创建一个子查询(即:b.id = a.id (+) AND c.id = a.id (+)不是可接受的子句)

那么外部连接链是这样的,a.b_id = b.id (+) AND b.c_id = c.id (+)我继承了这样的东西,我问如果a b连接为空,真正返回的是什么
Dan

@Dan您可以毫无问题地链接“旧样式”外部联接A -> B -> C。适用与标准ANSI连接相同的规则:如果外部连接链中的一个链接是常规连接,则该外部连接链将断开。换句话说A LEFT JOIN B INNER JOIN C等于A JOIN B JOIN C
Vincent Malgrat 2014年

4

在旧的Oracle join语法上使用ANSI语法的好理由之一是,几乎没有偶然创建笛卡尔乘积的机会。随着表数量的增加,有机会错过使用较旧的Oracle连接语法的隐式连接,但是,使用ANSI语法,您不能错过任何连接,因为您必须明确提及它们。

Oracle外部连接语法ANSI / ISO语法之间的区别。

左外连接-

SELECT e.last_name,
  d.department_name
FROM employees e,
  departments d
WHERE e.department_id = d.department_id(+);

SELECT e.last_name,
  d.department_name
FROM employees e
LEFT OUTER JOIN departments d
ON (e.department_id = d.department_id);

右外连接-

SELECT e.last_name,
  d.department_name
FROM employees e,
  departments d
WHERE e.department_id(+) = d.department_id;

SELECT e.last_name,
  d.department_name
FROM employees e
RIGHT OUTER JOIN departments d
ON (e.department_id = d.department_id);

外部连接-

在11gR1中对散列全外部联接的本地支持之前,Oracle将以以下方式在内部转换FULL OUTER JOIN-

SELECT e.last_name,
  d.department_name
FROM employees e,
  departments d
WHERE e.department_id = d.department_id(+)
UNION ALL
SELECT NULL,
  d.department_name
FROM departments d
WHERE NOT EXISTS
  (SELECT 1 FROM employees e WHERE e.department_id = d.department_id
  );

SELECT e.last_name,
  d.department_name
FROM employees e
FULL OUTER JOIN departments d
ON (e.department_id = d.department_id);

看看这个


1

Oracle(+)表示法仅在特定供应商的Oracle中使用。并且,ANSI标准的Join表示法可以在任何RDBMS中使用(例如Sql Server,MySql等)。否则,Oracle(+)表示法与ANSI标准的Join表示法之间没有区别。

如果在Sql查询中使用ANSI标准的联接表示法,则可以在任何RDBMS中使用相同的查询。而且,如果在这种情况下数据库从Oracle移植到任何其他RDBMS,则必须使用ANSI Syntax


1

我使用(+)表示法,因为几乎所有与Oracle Apps r12相关的查询都基于此。我没有在Oracle APPS查询(甚至是Oracle本身提供的查询)中看到带有标准“ join”表达式的单个SQL查询。如果您不相信我,只需在Google上搜索所有与Oracle应用程序相关的信息即可。例如:与固定资产有关的查询


1
  1. 使用显式JOIN而不是隐式JOIN(无论它们是否为外部联接)是因为意外地使用隐式联接创建笛卡尔乘积要容易得多。使用显式JOIN,您不能“偶然”创建一个。涉及的表越多,错过一个联接条件的风险就越高。
  2. 与ANSI联接相比,基本上(+)受严格限制。此外,它仅在Oracle中可用,而所有主要DBMS均支持ANSI连接语法
  3. 迁移到ANSI语法后,SQL不会开始表现出更好的性能-只是语法不同。
  4. Oracle强烈建议您使用前面示例中显示的更灵活的FROM子句连接语法。过去,ANSI语法中存在一些错误,但是如果您使用最新的11.2或12.1,则应该已经修复。
  5. 使用JOIN运算符可确保您的SQL代码符合ANSI,因此可以使前端应用程序更轻松地移植到其他数据库平台。
  6. 连接条件在每个表上具有非常低的选择性,而在理论叉积中对元组的选择性很高。where语句中的条件通常具有更高的选择性。
  7. Oracle在内部将ANSI语法转换为(+)语法,您可以在执行计划的“谓词信息”部分中看到这种情况。
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.