PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Überprüfen Sie, ob NULL im Postgres-Array vorhanden ist

Postgres 9.5 oder höher

Oder verwenden Sie array_position() . Grundsätzlich:

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

Siehe Demo unten.

Postgres 9.3 oder höher

Sie können mit den eingebauten Funktionen array_remove() testen oder array_replace() .

Postgres 9.1 oder eine andere Version

Wenn Sie wissen ein einzelnes Element, das niemals in Ihren Arrays existieren kann, können Sie dies schnell verwenden Ausdruck. Angenommen, Sie haben ein Array positiver Zahlen und -1 darf nie drin sein:

-1 = ANY(arr) IS NULL

Verwandte Antwort mit ausführlicher Erklärung:

  • Ist Array in PostgreSQL nur NULLen

Wenn Sie nicht absolut sicher sein können , Sie könnten Greifen Sie auf eines der teuren, aber sicheren zurück Methoden mit unnest() . Wie:

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

oder:

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

Aber Sie können schnell und sicher haben mit einem CASE Ausdruck. Verwenden Sie eine unwahrscheinliche Zahl und greifen Sie auf die sichere Methode zurück, falls diese existieren sollte. Vielleicht möchten Sie den Fall arr IS NULL behandeln separat. Siehe Demo unten.

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

Ergebnis:

 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

Beachten Sie, dass array_remove() und array_position() sind für mehrdimensionale Arrays nicht zulässig . Alle Ausdrücke rechts von t_93a funktionieren nur für 1-dimensionale Arrays.

db<>hier fummeln - Postgres 13, mit mehr Tests
Altes sqlfiddle

Benchmark-Setup

Die hinzugefügten Zeiten stammen aus einem Benchmark-Test mit 200.000 Zeilen in Postgres 9.5 . Dies ist mein Setup:

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;

Funktions-Wrapper

Zur wiederholten Verwendung , würde ich eine Funktion in Postgres 9.5 erstellen so:

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 nur für Postgres 9.6 oder höher.

Bei Verwendung eines polymorphen Eingabetyps funktioniert dies für beliebige Array-Typ, nicht nur int[] .

Machen Sie es IMMUTABLE Leistungsoptimierung und Indexausdrücke zuzulassen.

  • Unterstützt PostgreSQL "akzentunabhängige" Sortierungen?

Aber machen Sie es nicht STRICT , was "Funktions-Inlining" deaktivieren und die Leistung beeinträchtigen würde, da array_position() ist nicht STRICT selbst. Siehe:

  • Funktion wird ohne STRICT-Modifizierer schneller ausgeführt?

Wenn Sie den Fall arr IS NULL abfangen müssen :

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

Für Postgres 9.1 Verwenden Sie den t_91 Ausdruck von oben. Der Rest gilt unverändert.

Eng verwandt:

  • Wie kann man feststellen, ob NULL in einem Array in Postgres enthalten ist?