与此问题类似,如何查找数组中是否存在 NULL 值?
这里有一些尝试。
SELECT num, ar, expected, ar @> ARRAY[NULL]::int[] AS test1, NULL = ANY (ar) AS test2, array_to_string(ar, ', ') <> array_to_string(ar, ', ', '(null)') AS test3 FROM ( SELECT 1 AS num, '{1,2,NULL}'::int[] AS ar, true AS expected UNION SELECT 2, '{1,2,3}'::int[], false ) td ORDER BY num; num | ar | expected | test1 | test2 | test3 -----+------------+----------+-------+-------+------- 1 | {1,2,NULL} | t | f | | t 2 | {1,2,3} | f | f | | f (2 rows)
只有一个技巧array_to_string显示了预期值。有没有更好的方法来测试这个?
array_to_string
或使用array_position(). 基本上:
array_position()
SELECT array_position(arr, NULL) IS NOT NULL AS array_has_null
请参阅下面的演示。
您可以使用内置函数array_remove()或array_replace().
array_remove()
array_replace()
如果您知道数组中永远不会存在的单个元素,则可以使用此快速表达式。比如说,你有一个正数数组,并且-1永远不可能在其中:
-1
-1 = ANY(arr) IS NULL
如果您不能绝对确定,您可以使用一种昂贵但安全的方法unnest()。像:
unnest()
(SELECT bool_or(x IS NULL) FROM unnest(arr) x)
或者:
EXISTS (SELECT 1 FROM unnest(arr) x WHERE x IS NULL)
但是您可以快速安全地使用CASE表情。使用一个不太可能的数字,如果它应该存在,则回退到安全方法。您可能需要arr IS NULL单独处理该案例。请参阅下面的演示。
CASE
arr IS NULL
SELECT num, arr, expect , -1 = ANY(arr) IS NULL AS t_1 -- 50 ms , (SELECT bool_or(x IS NULL) FROM unnest(arr) x) AS t_2 -- 754 ms , EXISTS (SELECT 1 FROM unnest(arr) x WHERE x IS NULL) AS t_3 -- 521 ms , CASE -1 = ANY(arr) WHEN FALSE THEN FALSE WHEN TRUE THEN EXISTS (SELECT 1 FROM unnest(arr) x WHERE x IS NULL) ELSE NULLIF(arr IS NOT NULL, FALSE) -- catch arr IS NULL -- 55 ms -- ELSE TRUE -- simpler for columns defined NOT NULL -- 51 ms END AS t_91 , array_replace(arr, NULL, 0) <> arr AS t_93a -- 99 ms , array_remove(arr, NULL) <> arr AS t_93b -- 96 ms , cardinality(array_remove(arr, NULL)) <> cardinality(arr) AS t_94 -- 81 ms , COALESCE(array_position(arr, NULL::int), 0) > 0 AS t_95a -- 49 ms , array_position(arr, NULL) IS NOT NULL AS t_95b -- 45 ms , CASE WHEN arr IS NOT NULL THEN array_position(arr, NULL) IS NOT NULL END AS t_95c -- 48 ms FROM ( VALUES (1, '{1,2,NULL}'::int[], true) -- extended test case , (2, '{-1,NULL,2}' , true) , (3, '{NULL}' , true) , (4, '{1,2,3}' , false) , (5, '{-1,2,3}' , false) , (6, NULL , null) ) t(num, arr, expect);
结果:
num | arr | expect | t_1 | t_2 | t_3 | t_91 | t_93a | t_93b | t_94 | t_95a | t_95b | t_95c -----+-------------+--------+--------+------+-----+------+-------+-------+------+-------+-------+------- 1 | {1,2,NULL} | t | t | t | t | t | t | t | t | t | t | t 2 | {-1,NULL,2} | t | f --!! | t | t | t | t | t | t | t | t | t 3 | {NULL} | t | t | t | t | t | t | t | t | t | t | t 4 | {1,2,3} | f | f | f | f | f | f | f | f | f | f | f 5 | {-1,2,3} | f | f | f | f | f | f | f | f | f | f | f 6 | NULL | NULL | t --!! | NULL | f | NULL | NULL | NULL | NULL | f | f | NULL
请注意array_remove()和array_position()不允许用于多维数组。右侧的所有表达式t_93a仅适用于一维数组。
t_93a
db<>fiddle here - Postgres 13,带有更多测试 Old sqlfiddle
增加的时间来自Postgres 9.5 中 200k 行的基准测试。这是我的设置:
CREATE TABLE t AS SELECT row_number() OVER() AS num , array_agg(elem) AS arr , bool_or(elem IS NULL) AS expected FROM ( SELECT CASE WHEN random() > .95 THEN NULL ELSE g END AS elem -- 5% NULL VALUES , count(*) FILTER (WHERE random() > .8) OVER (ORDER BY g) AS grp -- avg 5 element per array FROM generate_series (1, 1000000) g -- increase for big test case ) sub GROUP BY grp;
对于重复使用,我会在 Postgres 9.5中创建一个函数,如下所示:
CREATE OR REPLACE FUNCTION f_array_has_null (anyarray) RETURNS bool LANGUAGE sql IMMUTABLE PARALLEL SAFE AS 'SELECT array_position($1, NULL) IS NOT NULL';
PARALLEL SAFE仅适用于 Postgres 9.6 或更高版本。
PARALLEL SAFE
使用多态输入类型,这适用于任何数组类型,而不仅仅是int[].
int[]
使其IMMUTABLE允许性能优化和索引表达式。
IMMUTABLE
但是不要这样做STRICT,这会禁用“函数内联”并损害性能,因为它本身array_position()不是STRICT。
STRICT
如果您需要抓住案件arr IS NULL:
CREATE OR REPLACE FUNCTION f_array_has_null (anyarray) RETURNS bool LANGUAGE sql IMMUTABLE PARALLEL SAFE AS 'SELECT CASE WHEN $1 IS NOT NULL THEN array_position($1, NULL) IS NOT NULL END';
对于 Postgres 9.1,使用t_91上面的表达式。其余部分不变。
t_91