Vérifier si NULL existe dans le tableau de Postgres
similaire à cette question , Comment puis-je trouver si une valeur nulle existe dans un tableau?
Voici quelques tentatives.
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)
seule une astuce avec array_to_string
montre la valeur attendue. Est-il un meilleur moyen de tester cela?
3 réponses
si vous sachez un seul élément qui ne peut jamais exister dans vos tableaux, vous pouvez utiliser cette fast expression dans Postgres 9.1 (ou n'importe quelle version de Postgres). Dites que vous avez un tableau de nombres positifs, donc -1
ne peut pas y être:
-1 = ANY(ar) IS NULL
réponse connexe avec explication détaillée:
Si vous ne peut pas être absolument sûr , vous pourrait revenir à l'un des cher, mais safe méthodes unnest()
. Comme:
(SELECT bool_or(x IS NULL) FROM unnest(ar) x)
ou:
EXISTS (SELECT 1 FROM unnest(ar) x WHERE x IS NULL)
mais vous pouvez avoir rapide et sûr avec un CASE
expression. Utilisez un nombre peu probable et revenez à la méthode sûre si elle devrait exister. Vous pouvez vouloir traiter le cas ar IS NULL
séparément. Voir démonstration ci-dessous.
Postgres 9.1 est en train de vieillir. Envisager la mise à niveau vers une version actuelle. En Postgres 9.3 ou plus tard, vous pouvez tester avec la fonction intégrée array_remove()
ou array_replace()
.
ou vous pouvez le faire fonctionner avec array_position()
dans Postgres 9.5 ou plus tard comme @Patrick fourni . J'ai ajouté des variantes améliorées ci-dessous.
Démo
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);
résultat:
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
noter que array_remove()
et array_position()
ne sont pas autorisés pour tableaux multidimensionnels . Toutes les expressions de la à droite de t_93a
seulement travailler pour les tableaux 1-dimensioal.
plus d'essais dans ce violon SQL (pour Postgres 9.3).
de Référence de l'installation
L'ajout de test de référence avec 200k lignes dans Postgres 9.5 . C'est ma configuration:
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;
fonction wrapper
pour utilisation répétée , je créerais une fonction dans Postgres 9.5 comme ceci:
CREATE OR REPLACE FUNCTION f_array_has_null (anyarray)
RETURNS bool LANGUAGE sql IMMUTABLE AS
'SELECT array_position(, NULL) IS NOT NULL';
utilisant un polymorphique type d'entrée cela fonctionne pour n'importe quel type de Tableau, pas seulement int[]
.
Faire IMMUTABLE
afin de permettre l'optimisation de la performance et d'expressions d'index.
mais ne le faites pas STRICT
, ce qui désactiverait la" fonction d'inclinaison " et nuirait aux performances.
si vous devez attraper le cas ar IS NULL
, au lieu de faire la fonction STRICT
, utilisez:
CREATE OR REPLACE FUNCTION f_array_has_null (anyarray)
RETURNS bool LANGUAGE sql IMMUTABLE AS
'SELECT CASE WHEN IS NOT NULL
THEN array_position(, NULL) IS NOT NULL END';
Pour Postgres 9.1 utilisez l'expression t_91
ci-dessus. Le reste s'applique inchangé.
question étroitement liée:
Postgresql's UNNEST ( ) fonction est un meilleur choix.Vous pouvez écrire une fonction simple comme ci-dessous pour vérifier les valeurs nulles dans un tableau.
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
par exemple,
SELECT NULL_EXISTS(array [1,2,NULL])
,NULL_EXISTS(array [1,2,3]);
résultat:
null_exists null_exists
----------- --------------
t f
ainsi, vous pouvez utiliser la fonction NULL_EXISTS()
dans votre requête comme ci-dessous.
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;
PostgreSQL 9.5 (je sais que vous avez précisé 9.1, mais de toute façon) a la fonction array_position()
pour faire exactement ce que vous voulez sans avoir à utiliser l'horriblement inefficace unnest()
pour quelque chose d'aussi trivial que celui-ci (voir 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)