PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Vérifier si NULL existe dans le tableau Postgres

Postgres 9.5 ou version ultérieure

Ou utilisez array_position() . En gros :

SELECT array_position(arr, NULL) IS NOT NULL AS array_has_null

Voir la démo ci-dessous.

Postgres 9.3 ou version ultérieure

Vous pouvez tester avec les fonctions intégrées array_remove() ou array_replace() .

Postgres 9.1 ou toute version

Si vous savez un seul élément qui ne peut jamais exister dans vos tableaux, vous pouvez utiliser ce rapide expression. Supposons que vous ayez un tableau de nombres positifs et -1 ne pourra jamais être dedans :

-1 = ANY(arr) IS NULL

Réponse connexe avec une explication détaillée :

  • Le tableau est-il entièrement NULL dans PostgreSQL

Si vous ne pouvez pas être absolument sûr , vous pourriez repliez-vous sur l'un des produits chers mais sûrs méthodes avec unnest() . Comme :

(SELECT bool_or(x IS NULL) FROM unnest(arr) x)

ou :

EXISTS (SELECT 1 FROM unnest(arr) x WHERE x IS NULL)

Mais vous pouvez avoir rapide et sûr avec un CASE expression. Utilisez un nombre improbable et revenez à la méthode sûre si elle doit exister. Vous voudrez peut-être traiter le cas arr IS NULL séparément. Voir la démo ci-dessous.

Démo

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);

Résultat :

 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

Notez que array_remove() et array_position() ne sont pas autorisés pour les tableaux multidimensionnels . Toutes les expressions à droite de t_93a ne fonctionne que pour les tableaux à 1 dimension.

db<>jouez ici - Postgres 13, avec plus de tests
Ancien sqlfiddle

Configuration de l'analyse comparative

Les temps ajoutés proviennent d'un test de référence avec 200 000 lignes dans Postgres 9.5 . Voici ma configuration :

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;

Enveloppe de fonction

Pour usage répété , 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 PARALLEL SAFE AS
 'SELECT array_position($1, NULL) IS NOT NULL';

PARALLEL SAFE uniquement pour Postgres 9.6 ou version ultérieure.

En utilisant un type d'entrée polymorphe, cela fonctionne pour tout type de tableau, pas seulement int[] .

Rendez-le IMMUTABLE pour permettre l'optimisation des performances et les expressions d'index.

  • PostgreSQL prend-il en charge les classements "insensibles aux accents" ?

Mais ne le faites pas STRICT , ce qui désactiverait la "fonction inlining" et nuirait aux performances car array_position() n'est pas STRICT lui-même. Voir :

  • La fonction s'exécute plus rapidement sans le modificateur STRICT ?

Si vous avez besoin d'attraper le cas 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';

Pour Postgres 9.1 utilisez le t_91 expression d'en haut. Le reste s'applique sans changement.

Etroitement lié :

  • Comment déterminer si NULL est contenu dans un tableau dans Postgres ?