【问题标题】:2 Outer Joins on Same Table?同一张表上的2个外部连接?
【发布时间】:2013-06-11 14:39:57
【问题描述】:

这是一个让我困惑了几天的问题,我搜索了又搜索,但找不到任何令人信服的答案!

简单的问题,为什么限制在 SQL 中使用 2 个外部联接,即使使用不同的列,在同一个表上,检查下面的查询以获得更好的理解。我也可以使用嵌套子查询或 ANSI 连接来克服它们,但是为什么它甚至首先使用 (+) 运算符受到限制!

在这个问题中,我指的是错误:

ORA-01417: 一个表最多可以外部连接到另一个表

我想问的是为什么允许这样做:

select * from
a, b, c
where a.a1 = b.b1
and a.a2 = c.c1

以及为什么不允许这样做:

select * from
a, b, c
where a.a1(+) = b.b1
and a.a2(+) = c.c1

请不要理会 ANSI 和嵌套子查询

【问题讨论】:

  • 你能提供一个你想要做什么的例子吗?您应该可以根据需要多次加入(innerouter)同一张表...
  • 正如@sgeddes 所说,您很可能会感到困惑。 Oracle 没有“2 个外部联接”限制
  • 我相信只有在使用 (+) 运算符进行“旧式”外连接时,您才会得到这个。不要使用 (+) 运算符 - 它是不可移植的,很可能被 Oracle 弃用。相反,使用 ANSI 样式的 LEFT OUTER JOIN 来执行连接。分享和享受。
  • @BobJarvis:外连接的旧语法(使用(+)已弃用,不应再使用。
  • @a_horse_with_no_name 当然不再推荐,即使是 Oracle 也不推荐。你确定它已被弃用吗?我在 Oracle 文档中找不到任何关于它的内容。

标签: sql oracle join


【解决方案1】:

Oracle 文档中描述了该限制:Outer Joins

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

...

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

这基本上意味着(在 ANSI/ISO 语法中描述)旧的(+) 语法不能拥有在 ANSI/ISO 中完全有效的东西:

--- Query 1 ---
  a 
RIGHT JOIN b
  ON a.x = b.x
RIGHT JOIN c 
  ON a.y = c.y

或:

--- Query 1b ---
  c 
LEFT JOIN 
    b LEFT JOIN a
        ON a.x = b.x 
  ON a.y = c.y

这只是旧 Oracle 语法的众多限制之一。


至于这种限制的原因,可能是实现细节或/和这种连接的模糊性。虽然上面的两个连接是 100% 等价的,但下面的不等价于上面的两个:

--- Query 2 ---
  a 
RIGHT JOIN c 
  ON a.y = c.y 
RIGHT JOIN b
  ON a.x = b.x 

查看 SQL-Fiddle 中的测试。 所以问题来了。应该如何将专有连接解释为查询 1 或 2?

FROM a, b, c 
WHERE a.y (+) = c.y 
  AND a.x (+) = b.x 

如果表出现在(2 个或更多)外部联接的左侧,则没有限制。即使使用旧语法,这些也是完全有效的:

FROM a
  LEFT JOIN b ON a.x = b.x 
  LEFT JOIN c ON a.y = c.y
  ...
  LEFT JOIN z ON a.q = z.q

FROM a, b, ..., z
WHERE a.x = b.x (+) 
  AND a.y = c.y (+)
  ...
  AND a.q = z.q (+)

【讨论】:

  • 这个限制在12c好像放宽了
  • @shonkylinuxuser 链接到这个?我认为他们会删除对旧语法的支持,而不是增强它。
  • @ypercube 链接是https://docs.oracle.com/database/121/NEWFT/chapter12101.htm#NEWFT492,但我的评论是基于我的经验。我有具有相同数据和架构的 10g 和 12c 数据库,具有多个 (+) 连接的查询在 12c 中有效,在 10g 中失败并出现上述 ORA-01417 错误。就我个人而言,当有多个连接表时,我发现旧语法更容易理解。
  • @shonkylinuxuser 有趣!你能分享一个在 12c 中工作但在 10g 中失败的查询的要点或 pastebin 吗?我仍然认为外连接的专有托盘语法(+) 是模棱两可的。我在 Oracle 文档中看不到他们已经解决了这个问题。
  • @LukaszSzozda thnx,我不知道。我会检查你的例子
【解决方案2】:

我强烈建议使用明确的OUTER JOIN 语法。从 Oracle 12c 开始,这个限制被放宽了1.4.3 Enhanced Oracle Native LEFT OUTER JOIN Syntax:

在 Oracle 数据库的早期版本中,在执行超过两对表的外连接的查询中,单个表可能是仅为另一个表生成的空表。 从 Oracle Database 12c 开始,单个表可以是多个表的 null 生成表。

代码:

CREATE TABLE a AS
SELECT 1 AS a1, 2 AS a2 FROM dual;

CREATE TABLE b AS
SELECT 1 AS b1 FROM dual;

CREATE TABLE c AS
SELECT 3 AS c1 FROM dual;

-- Oracle 12c: code below will work
SELECT * 
FROM a, b, c
WHERE a.a1(+) = b.b1
  AND a.a2(+) = c.c1;

输出:

A1  A2  B1  C1
-   -   1   3

db<>fiddle demo - Oracle 11g will return error

db<>fiddle demo Oracle 12c/18c will return resultset

【讨论】:

  • 文档不清楚。这样的join是什么意思?