PostgreSQL 用 array_length 等于 0 查不到空数组
PostgreSQL 空数组的 array_length 返回 NULL,等于 0 的条件因此漏选。改用 cardinality 判断元素数量,并区分空数组、NULL 数组和含 NULL 元素的数组。
表里确实存在空数组,WHERE ids = '{}'::bigint[] 能找到它们,换成 WHERE array_length(ids, 1) = 0 却一行也找不到。非空数组用 array_length(ids, 1) > 0 又能正常筛选,这容易让人误以为数据类型或驱动出了问题。
array_length 返回指定维度的长度。空数组没有可返回长度的那个维度,结果是 NULL,不是 0。NULL = 0 也不是 TRUE,所以这条 WHERE 条件不会保留空数组行。
如果要判断“数组是否没有元素”,PostgreSQL 9.4 及更新版本可以用 cardinality。它统计所有维度的元素总数,空数组返回 0。下面针对 PostgreSQL 原生数组,不适用于 JSON 数组或用逗号拼成的文本字段。
先看表达式结果,再调整筛选条件
这条只读示例不依赖业务表,用三种不同的数组值说明差别:
SELECT label,
ids,
array_length(ids, 1) AS first_dimension_length,
cardinality(ids) AS element_count,
ids IS NULL AS array_is_null
FROM (VALUES
('empty', ARRAY[]::bigint[]),
('null_array', NULL::bigint[]),
('one_null_element', ARRAY[NULL]::bigint[])
) AS samples(label, ids);
按这些函数的语义,三个结果应分别是:
| label | 第一维长度 | 元素总数 | 数组本身是否 NULL |
|---|---|---|---|
| empty | NULL | 0 | false |
| null_array | NULL | NULL | true |
| one_null_element | 1 | 1 | false |
一个元素值为 NULL 的数组仍然有一个元素,它不是空数组。数据库客户端可能把 SQL NULL 显示成空白,应结合 IS NULL 列判断,不要只凭界面的空白或花括号猜测。
业务查询中可先保留原有权限与租户过滤,仅把 array_length 和 cardinality 加到 SELECT 列表,查看少量已知记录。不要为了诊断取消全部筛选或把整张大表的数组导出到日志。
明确是否把 NULL 数组也纳入结果
只选择没有元素的数组,可以改成:
SELECT id, ids
FROM rises
WHERE cardinality(ids) = 0;
这里的 rises、id 和 ids 是示例名称。NULL 数组不会被这条条件选中。如果应用把“未填写”与“已填写但没有元素”分开存储,这种区别需要保留。
业务确实要求两者都选中时,明确写出两种情况:
SELECT id, ids
FROM rises
WHERE ids IS NULL OR cardinality(ids) = 0;
不要未经确认就把所有 NULL 更新成空数组;查询条件的修复不要求改写存储数据。COALESCE(cardinality(ids), 0) 会把 SQL NULL 归为 0,只有需要合并这两种含义时才适用。
如果只是判断指定数组等于空数组,原来的带类型比较也可以保留:
SELECT id, ids
FROM rises
WHERE ids = '{}'::bigint[];
类型应与实际列一致。这条查询也不会把 NULL 数组视为空数组。无需把数组转换成 JSON 或字符串,再比较显示出来的文本。
维度长度不能统一替换成元素总数
对于二维数组,array_length(values, 1) 表示第一维有多长,而 cardinality(values) 表示全部元素数量。例如:
SELECT array_length(ARRAY[[1, 2, 3], [4, 5, 6]], 1) AS rows,
cardinality(ARRAY[[1, 2, 3], [4, 5, 6]]) AS elements;
预期第一维长度为 2,总元素数为 6。修复“空不空”的判断时可使用 cardinality,但不要批量替换原本用于矩阵尺寸或特定维度校验的 array_length。
同样,array_length(ids, 2) 对一维非空数组也会返回 NULL,因为第二维不存在。若用 COALESCE(array_length(ids, 2), 0) = 0 判断整个数组为空,就会把这些非空数组也选进来。先确定自己需要的是维度长度还是总元素数。
验证空、NULL 和含空元素三种情况
保存原查询,在隔离数据或受控的只读查询中确认:空数组被选中,普通非空数组被排除,含一个 NULL 元素的数组仍按非空处理,SQL NULL 数组则遵循明确的业务选择。
修改后若筛选行数增加,应逐项解释新增记录属于哪一类,不能把“查到了行”当作完整验证。回退只需恢复查询代码,不需要重建数组列或删除记录。如果这条错误条件曾用于批处理、更新或导出,历史漏选的数据还需单独核对;修正 SELECT 不会自动补做此前遗漏的业务操作。


