同一张表进行两个外连接?

6
这里有一个问题困扰了我几天,我搜索了很多但找不到令人信服的答案!
简单来说,为什么在SQL中限制了对同一张表使用两个外连接,即使使用了不同的列,通过下面的查询可以更好地理解。虽然我可以使用嵌套子查询或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 和嵌套子查询。

6
你能否举个例子说明你想做什么?你应该能够使用“内连接”或“外连接”多次连接同一张表... - sgeddes
3
如@sgeddes所说,你很可能感到困惑。Oracle没有“2 Outer Joins”的限制。 - Lamak
5
如果您正在使用(+)运算符进行“旧式”外连接,我相信您才能理解这个问题。请勿使用(+)运算符——它不可移植,并且可能会被Oracle弃用。相反,请使用ANSI风格的LEFT OUTER JOIN来执行连接操作。分享并享受。 - Bob Jarvis - Слава Україні
3
@BobJarvis说:使用(+)进行外连接的旧语法已经被弃用,不应再使用。 - user330315
4
@a_horse_with_no_name Oracle现在不推荐使用它,即使是Oracle也是如此。您确定它已被弃用吗?我在Oracle文档中找不到任何信息。 - ypercubeᵀᴹ
显示剩余5条评论
2个回答

10

这个限制在Oracle文档中有详细描述:外连接

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

...

在执行超过两个表对的外连接查询中,单个表只能成为另一个表的null生成表。因此,您不能在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中已经放宽了。 - shonky linux user
@shonkylinuxuser 这个链接在哪里?我认为他们会移除旧语法的支持,而不是增强它。 - ypercubeᵀᴹ
1
@ypercube 这是链接 https://docs.oracle.com/database/121/NEWFT/chapter12101.htm#NEWFT492,但我的评论是基于我的经验。我有10g和12c数据库,它们具有相同的数据和模式,在12c中使用多个(+)连接的查询可以工作,在10g中失败并出现上述ORA-01417错误。 就个人而言,当有超过几个连接的表时,我发现旧语法更容易理解。 - shonky linux user
@shonkylinuxuser еҫҲжңүи¶ЈпјҒдҪ иғҪеҲҶдә«дёҖдёӘеңЁ12cдёӯжңүж•ҲдҪҶеңЁ10gдёӯеӨұиҙҘзҡ„жҹҘиҜўзҡ„gistжҲ–pastebinеҗ—пјҹжҲ‘д»Қ然и®ӨдёәеӨ–иҝһжҺҘзҡ„дё“жңүиҜӯжі•(+)жҳҜжңүжӯ§д№үзҡ„гҖӮжҲ‘еңЁOracleж–ҮжЎЈдёӯд№ҹжІЎжңүзңӢеҲ°д»–们解еҶідәҶиҝҷдёӘй—®йўҳгҖӮ - ypercubeᵀᴹ
@LukaszSzozda 谢谢,我不知道这个。我会看看你的示例。 - ypercubeᵀᴹ

1

我强烈建议使用明确的OUTER JOIN语法。从Oracle 12c开始,这个限制得到了放松 1.4.3增强的Oracle本机左外连接语法:

在以前的Oracle数据库版本中,在执行超过两个表对的外部联接的查询中,单个表只能为另一个表生成 null 。从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演示 - Oracle 11g将返回错误

db<>fiddle演示 - Oracle 12c/18c将返回结果集


文档不够清晰。这种连接的含义是什么? - philipxy

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