Answers:
为什么不能为了联接而将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)
另外,您可以使用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中尚不支持。
为了完整起见,我将提到该函数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文档中的参考位于“ 基本物化视图-为物化视图选择索引”。
您可以使用解码来连接空值:
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)
为什么不能在联接中使用空值?在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时,行才满足连接条件。
大多数关系数据库中的空值被认为是未知的。请勿将其与所有十六进制零混淆。如果某些内容包含null(未知),则无法进行比较。
Unknown = Known False
Unknown = Unknown False
Unknown >= Known False
Known >= Unknown False
这意味着,只要您在布尔表达式中将null作为操作数,else部分始终为true。
与开发人员对null的普遍仇恨相反,null占有一席之地。如果未知,请使用null。
UNKNOWN
而不是FALSE
;)
where (a = b or (a is null and b is null))
sys_op_map_nonnull