【问题标题】:Outer Join not working as expected in Oracle外部联接在 Oracle 中未按预期工作
【发布时间】:2017-08-03 13:19:21
【问题描述】:
  • 我有一个客户主表CUSTOMER_MASTER,它有一个主键CUSTOMER_ID
  • 还有两个客户子表CUSTOMER_DETAILS_1CUSTOMER_DETAILS_2每个表都有自己的主键和外键CUSTOMER_ID
  • 并非主表CUSTOMER_MASTER中的所有记录在上述两个子表中都有对应的记录。

我需要构建一个 select 语句,它基于键 CUSTOMER_ID 连接三个表 CUSTOMER_MASTERCUSTOMER_DETAILS_1CUSTOMER_DETAILS_2。但问题是CUSTOMER_MASTER 中子表中没有相应记录的记录即使使用外连接也不会出现在输出中。请参阅以下查询:

SELECT * 
FROM CUSTOMER_MASTER MAS, CUSTOMER_DETAILS_1 SUB1, CUSTOMER_DETAILS_2 SUB2
WHERE SUB1.CUSTOMER_ID = MAS.CUSTOMER_ID(+)
AND   SUB2.CUSTOMER_ID = MAS.CUSTOMER_ID(+)
-- Other conditions for CUSTOMER_DETAILS_1 and CUSTOMER_DETAILS_2 are there.

我的目标是选择所有客户,如下所示:

数据库是 Oracle 11g

【问题讨论】:

  • 如果您使用 Oracle 专有的、旧的、不推荐的、不推荐的连接表示法,您应该正确使用它。要像您正在做的那样获得外连接,(+)应该在“缺失”行的一侧(要记住它,将(+)视为表示在该表中添加了假行,在一侧+)。
  • 我也在另一边添加了(+),我得到了相同的结果。@mathguy
  • 从不使用隐式连接开始。
  • 好的,我想我知道发生了什么。如果您必须使用旧的 Oracle 语法...您可能有类似... and sub1.start_date > sysdate - 100 的条件。在这种情况下,您也必须将 (+) 附加到左侧!即使它不是连接条件。每次提及明细表都必须在末尾附加 (+)。
  • @mathguy 好吧...每次提及您希望将其视为加入/打开条件与 where 条件。但最好只使用现代语法。

标签: sql oracle join


【解决方案1】:

不要使用过时的旧连接语法。它不太强大(有些东西你不能用这种方式表达),它可能是模棱两可的(当一个条件是 JOIN 的一部分,什么时候它是 WHERE 子句的一部分,或者条件属于哪个 JOIN 时,并不总是很清楚),并且更难阅读,因为它将查询中的所有条件混合在一起。至少自 1999 年以来,它或多或少已经过时了。

在这种情况下,“1&2 的其他条件”部分很可能是问题所在。由于您在 JOIN 和 WHERE 子句之间没有区别,因此将它们作为 WHERE 子句应用。但是在CUSTOMER_DETAILS_1CUSTOMER_DETAILS_2 中没有记录的情况下,条件是与NULL 进行比较,这实际上导致FALSE,并且整个CUSTOMER_MASTER 记录被排除在结果集中。

使用旧语法并不总是能很好地解决这个问题,但使用现代连接语法很容易。这可能会按您的预期工作:

SELECT * 
FROM CUSTOMER_MASTER MAS
LEFT JOIN CUSTOMER_DETAILS_1 SUB1 ON SUB.CUSTOMER_ID = MAS.CUSTOMER_ID
     -- AND other conditions for CUSTOMER_DETAILS_1
LEFT JOIN CUSTOMER_DETAILS_2 SUB2 ON SUB2.CUSTOMER_ID = MAS.CUSTOMER_ID 
     -- AND other conditions for CUSTOMER_DETAILS_2

这会强制将附加条件视为 JOIN 而不是 WHERE 的一部分,这样它们就不会导致 CUSTOMER_MASTER 记录被排除。

【讨论】:

  • 一般来说你是对的,但是至少在一种情况下你仍然必须使用旧的 Oracle 连接语法:对于具有 FAST REFRESH 的 MATERIALIZED VIEWs。如果在查询中使用 ANSI 连接,则不能使用 FAST REFRESH。
  • @WernfriedDomscheit 这是甲骨文真正需要解决的问题。但与此同时,我更新了答案以解释为什么它对这个查询很重要。
  • 嗯,也有这个问题。但是当他解决这个问题时,他仍然必须处理那些额外的条件。这解决了这两个问题。
  • 很久以前,我在 Oracle 提出了一张票,原因是:Oracle 不认为这是一个错误,而只是因为缺乏文档!我不知道当前 Oracle 版本的情况如何。至少在 12.1 中没有解决。
  • @JoelCoehoorn - 好的,直到现在我还没有详细阅读这个问题。我确实看到了这个问题......我在附加评论中进一步解释了。
【解决方案2】:

试试下面的查询。您可以在 sqlfiddler 上查看架构和结果 http://sqlfiddle.com/#!4/40da3/3

SELECT 
    c.CustomerId,
    c.MasterDetails,
    d1.Details1,
    d2.Details2
FROM
                  Customers c
  LEFT OUTER JOIN CustomerDetails1 d1
          ON d1.CustomerId = c.CustomerId
  LEFT OUTER JOIN CustomerDetails2 d2
          ON d2.CustomerId = c.CustomerId
WHERE
    --Additonal where clause here
        1=1
    AND 2=2
    --etc
ORDER BY 
  c.CustomerId

【讨论】:

  • 我认为 Joel 打败了我……我唯一的建议是我真的不喜欢在 join 部分包含任何 where 子句类型的东西。我更喜欢让连接部分成为链接表的实际连接,而 where 子句实际上限制或过滤记录。
  • WHERE 子句更好,除非它具有不同的含义。将这些条件移至 JOIN/ON 的目的是避免排除他想要保留的记录。
  • 乔尔所说的绝对正确。您可以将单表条件放在 WHERE 子句或 JOIN 子句中。在内连接中没有区别,但在外连接中却有。将条件放入 WHERE 与 JOIN 有不同的用途并解决不同的问题。
猜你喜欢
  • 2012-08-05
  • 2013-08-06
  • 1970-01-01
  • 2020-08-06
  • 1970-01-01
  • 1970-01-01
  • 2015-06-17
  • 1970-01-01
  • 2016-08-31
相关资源
最近更新 更多