Oracle (+) 外连接和常量值

Oracle (+) outer join and constant values

我 运行 遇到了一个问题,我不知道如何正确配置连接。我正在使用报告软件,该软件在我们的 Oracle 数据库的 WHERE 子句中使用 (+) 指示符。我有两个表:

支票和交易。一张支票可以有多个交易,但一个交易不一定有对应的支票。

两个表都有标识当前记录的指示符,称为 CURRENT,它是 'Y' 或 'N'。

加入选项 1:

Select *
FROM TXN,CHK
WHERE
TXN.CHK_ID = CHK.CHK_ID(+)
and TXN.CURRENT = 'Y'
and CHK.CURRENT = 'Y'

加入选项 2:

Select *
FROM TXN,CHK
WHERE
TXN.CHK_ID = CHK.CHK_ID(+)
and TXN.CURRENT = 'Y'
and CHK.CURRENT(+) = 'Y'

这些联接产生不同的结果,我似乎无法弄清楚额外的外部联接指示器在应用于 CHK.CURRENT 字段时会产生什么影响。带有额外指示符的查询会产生更大的结果集。有人可以帮忙解释一下这是怎么回事吗?

我将使用等效的 "ANSI JOIN" 语法来解释这一点:

选项 1

SELECT *
FROM TXN
LEFT JOIN CHK 
  ON TXN.CHK_ID = CHK.CHK_ID
WHERE TXN.CURRENT = 'Y'
AND CHK.CURRENT = 'Y'

选项 2

SELECT *
FROM TXN
LEFT JOIN CHK 
  ON TXN.CHK_ID = CHK.CHK_ID 
  AND CHK.CURRENT = 'Y'
WHERE TXN.CURRENT = 'Y'

如您所见,在选项 1 中,在 指定 LEFT JOIN table 表达式后,即在 的结果上应用常量谓词LEFT JOIN.

在选项 2 中,您的常量谓词之一是 LEFT JOIN 表达式的一部分。

LEFT JOIN 是如何工作的?

LEFT JOIN 的想法是它将 return 来自 JOIN 表达式的 LEFT 侧的所有行,无论是否给定连接谓词,另一侧有一个匹配行。因此,在选项 2 中,无论您是否在 CHK 中找到一行 TXN 中的一行 CURRENT = 'Y'TXN 中的行仍然是 returned .这就是您在选项 2 中获得更多行的原因。

另外,这个例子应该解释为什么你应该更喜欢 "ANSI JOIN" 语法。从维护/可读性的角度来看,您的查询在做什么更加清楚。

(+) 运算符告诉 Oracle 谓词是外部连接的一部分,而不是可以在连接后应用的过滤谓词。使用 SQL 99 外连接语法,第一个查询等同于

SELECT *
  FROM txn
       left outer join chk 
         on( txn.chk_id = chk.chk_id )
 WHERE txn.current = 'Y'
   AND chk.current = 'Y'

而第二个相当于

SELECT *
  FROM txn
       left outer join chk 
         on( txn.chk_id  = chk.chk_id AND
             chk.current = 'Y')
 WHERE txn.current = 'Y'

从逻辑上讲,在第一种情况下,您进行了外部联接,但随后 chk.currentNULL 的所有行都被过滤掉了。在第二种情况下,chk.current = 'Y' 条件不会过滤掉任何行,它只是控制是否在 chk 中找到匹配的行或者是否执行左外连接。

加入选项 1 将仅考虑 CHK.CURRENT = 'Y' 的那些行。因此,如果事务没有校验,CHK.CURRENT将为NULL,该行将不在结果集中。

加入选项 2 将考虑那些 CHK.CURRENT 的行,如果有检查,是 'Y'。如果事务没有检查,则不会应用此测试,并且该行将在结果集中。

你可以通过这个比较看出区别:

Select *
FROM TXN,CHK
WHERE
TXN.CHK_ID = CHK.CHK_ID(+)
and TXN.CURRENT = 'Y'
and CHK.CURRENT(+) = 'Y'
MINUS
Select *
FROM TXN,CHK
WHERE
TXN.CHK_ID = CHK.CHK_ID(+)
and TXN.CURRENT = 'Y'
and CHK.CURRENT = 'Y'

加入选项 1 相当于:

SELECT *
FROM   TXN
       LEFT OUTER JOIN CHK
       ON ( TXN.CHK_ID = CHK.CHK_ID )
WHERE  TXN.CURRENT = 'Y'
AND    CHK.CURRENT = 'Y'

但是,由于结果集中的任何行都将具有 CHK.CURRENT = 'Y'(非空值),因此这些行必须在 CHK_ID 上匹配并且查询实际上等同于:

SELECT *
FROM   TXN
       INNER JOIN CHK
       ON ( TXN.CHK_ID = CHK.CHK_ID )
WHERE  TXN.CURRENT = 'Y'
AND    CHK.CURRENT = 'Y'

加入选项 2 相当于:

SELECT *
FROM   TXN
       LEFT OUTER JOIN CHK
       ON (    TXN.CHK_ID  = CHK.CHK_ID
           AND CHK.CURRENT = 'Y' )
WHERE  TXN.CURRENT = 'Y'

您可以使用以下方法使加入选项 1 等同于选项 2:

SELECT *
FROM   TXN
       LEFT OUTER JOIN CHK
       ON ( TXN.CHK_ID = CHK.CHK_ID )
WHERE  TXN.CURRENT = 'Y'
AND    ( CHK.CURRENT = 'Y' OR CHK.CHK_ID IS NULL )