与this question类似,如何查找数组中是否存在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
的技巧才能显示预期值。有没有更好的方法来测试这个?
如果您知道数组中永远不存在的单个元素,则可以在Postgres 9.1(或任何Postgres版本)中使用此快速表达式。假设你有一组正数,所以-1
不能在其中:
-1 = ANY(ar) IS NULL
相关答案详细说明:
如果你不能完全确定,你可以回到使用unnest()
的昂贵但安全的方法之一。喜欢:
(SELECT bool_or(x IS NULL) FROM unnest(ar) x)
要么:
EXISTS (SELECT 1 FROM unnest(ar) x WHERE x IS NULL)
但是你可以快速安全地使用CASE
表达。使用不太可能的数字,如果它存在,则回退到安全方法。您可能希望单独处理案例ar IS NULL
。见下面的演示。
在Postgres 9.3或更高版本中,您可以使用内置函数array_remove()
或array_replace()
进行测试。
或者在Postgres 9.5或更高版本中使用array_position()
,如@Patrick added。我包括改进的变种。
SELECT num, ar, expect
, -1 = ANY(ar) IS NULL AS t_1 -- 50 ms
, (SELECT bool_or(x IS NULL) FROM unnest(ar) x) AS t_2 -- 754 ms
, EXISTS (SELECT 1 FROM unnest(ar) x WHERE x IS NULL) AS t_3 -- 521 ms
, CASE -1 = ANY(ar)
WHEN FALSE THEN FALSE
WHEN TRUE THEN EXISTS (SELECT 1 FROM unnest(ar) x WHERE x IS NULL)
ELSE NULLIF(ar IS NOT NULL, FALSE) -- catch ar IS NULL -- 55 ms
-- ELSE TRUE -- simpler for columns defined NOT NULL -- 51 ms
END AS t_91
, array_replace(ar, NULL, 0) <> ar AS t_93a -- 99 ms
, array_remove(ar, NULL) <> ar AS t_93b -- 96 ms
, cardinality(array_remove(ar, NULL)) <> cardinality(ar) AS t_94 -- 81 ms
, COALESCE(array_position(ar, NULL::int), 0) > 0 AS t_95a -- 49 ms
, array_position(ar, NULL) IS NOT NULL AS t_95b -- 45 ms
, CASE WHEN ar IS NOT NULL
THEN array_position(ar, 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, ar, expect);
结果:
num | ar | 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
右侧的所有表达式仅适用于1维数组。
db <>小提琴here(Postgres 11,有更多测试)。 Postgres的旧sqlfiddle 9.6。
增加的时间来自Postgres 9.5中的20万行基准测试。这是我的设置:
CREATE TEMP TABLE t AS
SELECT row_number() OVER() AS num
, array_agg(elem) AS ar
, 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 AS
'SELECT array_position($1, NULL) IS NOT NULL';
使用polymorphic输入类型,这适用于任何数组类型,而不仅仅是int[]
。
使它成为IMMUTABLE
以允许性能优化和索引表达式。
但是不要让它成为STRICT
,这将禁用“功能内联”并削弱性能。
如果您需要捕获案例ar IS NULL
,而不是使用STRICT
函数,请使用:
CREATE OR REPLACE FUNCTION f_array_has_null (anyarray)
RETURNS bool LANGUAGE sql IMMUTABLE AS
'SELECT CASE WHEN $1 IS NOT NULL
THEN array_position($1, NULL) IS NOT NULL END';
对于Postgres 9.1,请使用上面的t_91
表达式。其余的应用不变。
密切相关:
PostgreSQL 9.5(我知道你spcified 9.1,但无论如何)有array_position()
函数来做你想要的,而不必使用非常低效的unnest()
这样的事情(参见test4
):
patrick@puny:~$ psql -d test
psql (9.5.0)
Type "help" for help.
test=# 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,
coalesce(array_position(ar, NULL::int), 0) > 0 AS test4
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 | test4
-----+------------+----------+-------+-------+-------+-------
1 | {1,2,NULL} | t | f | | t | t
2 | {1,2,3} | f | f | | f | f
(2 rows)
PostgreSQL的UNNEST(函数是一个更好的选择。你可以编写一个像下面这样的简单函数来检查数组中的NULL值。
create or replace function NULL_EXISTS(val anyelement) returns boolean as
$$
select exists (
select 1 from unnest(val) arr(el) where el is null
);
$$
language sql
例如,
SELECT NULL_EXISTS(array [1,2,NULL])
,NULL_EXISTS(array [1,2,3]);
结果:
null_exists null_exists
----------- --------------
t f
因此,您可以在查询中使用NULL_EXISTS()
函数,如下所示。
SELECT num, ar, expected,NULL_EXISTS(ar)
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;
我用这个
select
array_position(array[1,null], null) is not null
array_position - 返回数组中第一个第二个参数出现的下标,从第三个参数指示的元素开始或在第一个元素处返回(数组必须是一维的)