sql >> Database teknologi >  >> RDS >> PostgreSQL

Tjek, om NULL findes i Postgres-arrayet

Postgres 9.5 eller nyere

Eller brug array_position() . Grundlæggende:

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

Se demoen nedenfor.

Postgres 9.3 eller nyere

Du kan teste med de indbyggede funktioner array_remove() eller array_replace() .

Postgres 9.1 eller en hvilken som helst version

Hvis du ved det et enkelt element, der aldrig kan eksistere i dine arrays, kan du bruge denne hurtige udtryk. Lad os sige, at du har en række positive tal og -1 kan aldrig være i det:

-1 = ANY(arr) IS NULL

Relateret svar med detaljeret forklaring:

  • Er array alle NULL'er i PostgreSQL

Hvis du ikke kan være helt sikker , du kunne fald tilbage til en af ​​de dyre, men sikre metoder med unnest() . Ligesom:

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

eller:

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

Men du kan få hurtigt og sikkert med en CASE udtryk. Brug et usandsynligt tal og fald tilbage til den sikre metode, hvis den skulle eksistere. Du ønsker måske at behandle sagen arr IS NULL separat. Se demoen nedenfor.

Demo

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

Resultat:

 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

Bemærk at array_remove() og array_position() er ikke tilladt for flerdimensionelle arrays . Alle udtryk til højre for t_93a virker kun for 1-dimensionelle arrays.

db<>spil her - Postgres 13, med flere test
Gamle sqlfiddle

Benchmark-opsætning

De tilføjede tider er fra en benchmark-test med 200.000 rækker i Postgres 9.5 . Dette er min opsætning:

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;

Funktionsindpakning

Til gentagen brug , ville jeg oprette en funktion i Postgres 9.5 sådan her:

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 kun til Postgres 9.6 eller nyere.

Ved at bruge en polymorf inputtype virker dette for alle matrixtype, ikke kun int[] .

Gør det IMMUTABLE for at tillade ydeevneoptimering og indeksudtryk.

  • Understøtter PostgreSQL "accentufølsomme" sammenstillinger?

Men gør det ikke til STRICT , hvilket ville deaktivere "function inlining" og forringe ydeevnen, fordi array_position() er ikke STRICT sig selv. Se:

  • Funktionen udføres hurtigere uden STRICT modifier?

Hvis du har brug for at fange sagen 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';

Til Postgres 9.1 brug t_91 udtryk fra oven. Resten gælder uændret.

Nært beslægtet:

  • Hvordan bestemmer man, om NULL er indeholdt i et array i Postgres?


  1. Oracle Slet rækker, der matcher flere værdier

  2. Udfør lagret procedure fra en funktion

  3. De fandens store genstande

  4. Forskellen mellem to datoer i MySQL