为什么不能在联接中使用空值?


13

我已经通过使用...解决了查询问题,row_number() over (partition by这是一个更笼统的问题,为什么我们不能在联接中使用具有空值的列。为什么不能为了联接而将null等于null?

Answers:


31

为什么不能为了联接而将null等于null?

只需告诉Oracle即可:

select *
from one t1 
  join two t2 on coalesce(t1.id, -1) = coalesce(t2.id, -1);

(请注意,在标准SQL中,您可以t1.id is not distinct from t2.id用来获取一个空安全的相等运算符,但Oracle不支持该运算符)

但这仅在替换值(上例中为-1)未真正出现在表中的情况下有效。对数字找到这样一个“神奇”的价值也许是可能的,但是这将是字符值非常困难的(尤其是因为甲骨文对待空字符串null为好)

加:不会在id列上使用索引(尽管您可以使用coalesce()表达式定义基于函数的索引)。

适用于所有类型的另一个选项,没有魔术值:

              on t1.id = t2.id or (t1.id is null and t2.id is null)

真正的问题是:这有意义吗?

考虑以下样本数据:

表一

id
----
1
2
(null)
(null)

表二

id
----
1
2
(null)
(null)
(null)

在连接中应选择空值组合的哪一个?我上面的示例将导致对所有空值进行交叉联接。

T1_ID  | T2_ID 
-------+-------
     1 |      1
     2 |      2
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)
(null) | (null)

6

另外,您可以使用INTERSECT相等运算符使两个null彼此匹配:

SELECT
  *
FROM
  t1
  INNER JOIN t2
    ON EXISTS (SELECT t1.ID FROM DUAL INTERSECT SELECT t2.ID FROM DUAL)
;

有关说明,请参见此DBFiddle演示

当然,尽管它实际上并不比BriteSponge的建议长很多,但看起来却很令人吃惊。但是,请原谅双关语,这绝对不是匹配前面提到的以注释标准方式简明的方式,这是IS NOT DISTINCT FROM运算符,Oracle中尚不支持。


2

为了完整起见,我将提到该函数SYS_OP_MAP_NONNULL现在可以安全地用于比较12c文档中记录的空值。这意味着Oracle不仅会随机删除它并破坏您的代码。

SELECT *
FROM   one t1 
       JOIN two t2
         ON SYS_OP_MAP_NONNULL(t1.id) = SYS_OP_MAP_NONNULL(t2.id)

好处是您不会遇到“魔术”数问题。

Oracle文档中的参考位于“ 基本物化视图-为物化视图选择索引”


所以现在有文件记录了吗?因为AskTom(在2003年)曾说过:-没有记录,因此存在where (a = b or (a is null and b is null))sys_op_map_nonnull
ypercubeᵀᴹ

如果您有链接,请将其添加到问题中。我没有在12c函数中找到提及,但搜索Oracle文档和特定版本相当困难。
ypercubeᵀᴹ

2

您可以使用解码来连接空值:

on decode(t1.id, t2.id, 1, 0) = 1

decode将null视为相等,因此可以在没有“魔术”数字的情况下使用。两列必须具有相同的数据类型。

它不会使代码更具可读性,但可能仍比 t1.id = t2.id or (t1.id is null and t2.id is null)


1

为什么不能在联接中使用空值?在Oracle中,以下两项均不等于true:

  • NULL = NULL
  • NULL <> NULL

这就是为什么我们IS NULL/ IS NOT NULL以检查空值。
要对此进行测试,您可以简单地执行以下操作:

SELECT * FROM table_name WHERE NULL = NULL

联接正在评估布尔条件,并且它们没有对它们进行编程以使其操作不同。您可以在连接条件中添加大于号,并添加其他条件;它只是将其评估为布尔表达式。

我想为了一致性,连接中的null不能等于null。这将违背比较运算符的通常行为。


NULL = anything结果是NULL因为SQL标准是这样说的。仅当表达式为true时,行才满足连接条件。
Laurenz Albe

1
除了文字上的实现细节(情况并非总是如此:某些DB可以出于某些/所有目的而选择将NULL等同于NULL)之外,还有一个逻辑原因:NULL是未知的。当您将NULL与NULL比较时,您会问“这个未知事物是否等于另一个未知事物”,唯一的合理答案是“未知”-另一个NULL(在比较情况下映射为false)。
David Spillett

-4

大多数关系数据库中的空值被认为是未知的。请勿将其与所有十六进制零混淆。如果某些内容包含null(未知),则无法进行比较。

Unknown = Known False
Unknown = Unknown False
Unknown >= Known False
Known >= Unknown False

这意味着,只要您在布尔表达式中将null作为操作数,else部分始终为true。

与开发人员对null的普遍仇恨相反,null占有一席之地。如果未知,请使用null。


6
实际上,您进行的所有示例比较都是yield UNKNOWN而不是FALSE;)
ypercubeᵀᴹ17年

您是对的,但是布尔表达式的目的是仅得出true或false,因此,在这里不要发疯:)。
jujiro
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.