跳到主要内容

equal_null:NULL 安全的等值比较

速查结论

equal_null(expr1, expr2) 是 Spark SQL 中用于NULL 安全等值比较的函数。与普通 = 运算符不同,当两个操作数都为 NULL 时返回 true,只有一个为 NULL 时返回 false,都不为 NULL 时行为与 = 一致。简单来说:equal_null 永远返回 true 或 false,不会返回 NULL。

语法

equal_null(expr1, expr2)

参数说明

参数说明
expr1待比较的第一个表达式
expr2待比较的第二个表达式

返回值

  • 两个操作数都为 NULL → true
  • 只有一个操作数为 NULL → false
  • 都不为 NULL → 与 = 结果一致

类型要求:两个表达式必须是相同类型或可以强制转换为共同类型,必须是可用于等值比较的类型。不支持 Map 类型。对于复杂类型(数组/结构体),字段的数据类型必须是可排序的。

Since: 3.4.0

示例

基础比较

-- 都不为 NULL:行为与 = 一致
SELECT equal_null(3, 3);
-- 结果: true

SELECT equal_null(3, 5);
-- 结果: false

NULL 安全比较

-- 两个都为 NULL:返回 true(区别于 = 返回 NULL)
SELECT equal_null(NULL, NULL);
-- 结果: true

-- 只有一个为 NULL:返回 false(区别于 = 返回 NULL)
SELECT equal_null(true, NULL);
-- 结果: false

SELECT equal_null(NULL, 'abc');
-- 结果: false

类型不匹配

-- 类型不同且无法隐式转换:返回 false
SELECT equal_null(1, '11');
-- 结果: false

equal_null vs = 运算符行为对比

这是理解 equal_null 的关键——三个场景的对比:

场景A = Bequal_null(A, B)
都不为 NULL,值相等truetrue
都不为 NULL,值不等falsefalse
都为 NULLNULLtrue
一个为 NULLNULLfalse
-- 直观对比表
WITH test AS (
SELECT 1 AS a, 1 AS b UNION ALL
SELECT 1, 2 UNION ALL
SELECT NULL, NULL UNION ALL
SELECT 1, NULL
)
SELECT
a,
b,
a = b AS equal_operator,
equal_null(a, b) AS equal_null_func
FROM test;
-- 结果:
-- a b equal_operator equal_null_func
-- 1 1 true true
-- 1 2 false false
-- NULL NULL NULL true -- 区别在这里
-- 1 NULL NULL false -- 区别在这里

equal_null vs IS DISTINCT FROM 对比

函数都为 NULL一个为 NULL返回值特征
equal_null(A, B)truefalse比较"是否相等",永远返回 true/false
A IS NOT DISTINCT FROM Btruefalse与 equal_null 逻辑一致,NULL 视为相等
A IS DISTINCT FROM Bfalsetrue与 IS NOT DISTINCT FROM 取反
-- equal_null 与 IS NOT DISTINCT FROM 语义等价
SELECT equal_null(NULL, NULL);
-- 结果: true

SELECT NULL IS NOT DISTINCT FROM NULL;
-- 结果: true

-- IS DISTINCT FROM 是取反
SELECT NULL IS DISTINCT FROM NULL;
-- 结果: false

选型建议

  • 需要 NULL 安全比较且永远返回 true/false:用 equal_null
  • 偏好 ANSI SQL 标准语法:用 IS NOT DISTINCT FROM
  • 需要找出"不同"的行(含 NULL 比较):用 IS DISTINCT FROM

常见报错与避坑指南

Map 类型不支持

equal_null 不支持 Map 类型的比较,传入 Map 类型会报错。

-- 错误写法:Map 类型不支持
SELECT equal_null(map('a', 1), map('a', 1));
-- AnalysisException: cannot resolve 'equal_null(map('a', 1), map('a', 1))'
-- equal_null does not support map type

-- 替代方案:先将 Map 转为 JSON 字符串再比较
SELECT equal_null(to_json(map('a', 1)), to_json(map('a', 1)));

复杂类型的字段必须可排序

对于包含嵌套字段的复杂类型(如 struct),字段数据类型必须支持排序,否则可能报错。

-- 错误写法:struct 中包含不可排序的字段类型
SELECT equal_null(
named_struct('name', 'a', 'data', map('k', 'v')),
named_struct('name', 'a', 'data', map('k', 'v'))
);
-- 可能报错:data type mismatch

-- 正确写法:确保复杂类型的字段是可排序的基本类型
SELECT equal_null(
named_struct('name', 'a', 'value', 1),
named_struct('name', 'a', 'value', 1)
);
-- 结果: true

与普通 = 运算符的 NULL 传播陷阱

在 WHERE 条件中,= 遇到 NULL 会直接排除该行,而 equal_null 会返回 false(而非 NULL),这可能导致不同的过滤结果。

-- 数据准备
WITH test AS (
SELECT 1 AS id, 'A' AS tag UNION ALL
SELECT 2 AS id, NULL AS tag UNION ALL
SELECT 3 AS id, 'A' AS tag
)

-- = 运算符:NULL 行被排除(因为 NULL = 'A' 结果为 NULL,被视为 false)
SELECT * FROM test WHERE tag = 'A';
-- 结果: id=1, id=3

-- equal_null:明确返回 false,同样排除 NULL 行,但语义更清晰
SELECT * FROM test WHERE equal_null(tag, 'A');
-- 结果: id=1, id=3(与上面相同,但不会产生 =NULL 的语义歧义)

在过滤场景中,如果只是想筛选特定值,使用 = 是安全的(NULL 行自然被排除)。equal_null 的优势在 JOIN 条件和需要明确判断"两个值是否相等(包括 NULL)"的场景中。

📱关注公众号

「数据仓库技术」文章同步更新,不错过每一篇干货

微信公众号二维码
💬加群交流

备注「数据仓库技术」加入社群,每日一道大厂SQL真题

交流微信二维码

你可能还想看