sql >> Databasteknik >  >> RDS >> PostgreSQL

Kontrollera om NULL finns i Postgres-arrayen

Postgres 9.5 eller senare

Eller använd array_position() . I grund och botten:

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

Se demo nedan.

Postgres 9.3 eller senare

Du kan testa med de inbyggda funktionerna array_remove() eller array_replace() .

Postgres 9.1 eller någon version

Om du vet ett enda element som aldrig kan existera i dina arrayer, kan du använda denna snabb uttryck. Säg att du har en rad positiva tal och -1 kan aldrig vara med i den:

-1 = ANY(arr) IS NULL

Relaterat svar med detaljerad förklaring:

  • Är array alla NULLs i PostgreSQL

Om du inte kan vara helt säker , du kunde fall tillbaka till en av de dyra men säkra metoder med unnest() . Gilla:

(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 ha snabbt och säkert med ett CASE uttryck. Använd ett osannolikt tal och fall tillbaka till den säkra metoden om den skulle finnas. Du kanske vill behandla fallet arr IS NULL separat. Se demo nedan.

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

Observera att array_remove() och array_position() är inte tillåtna för flerdimensionella arrayer . Alla uttryck till höger om t_93a fungerar endast för 1-dimensionella arrayer.

db<>spela här - Postgres 13, med fler tester
Old sqlfiddle

Konfiguration av benchmark

De tillagda tiderna är från ett benchmarktest med 200 000 rader i Postgres 9.5 . Det här är min inställning:

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;

Funktionsomslag

För upprepad användning , skulle jag skapa en funktion i Postgres 9.5 så här:

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 endast för Postgres 9.6 eller senare.

Genom att använda en polymorf inmatningstyp fungerar detta för alla matristyp, inte bara int[] .

Gör det IMMUTABLE för att tillåta prestandaoptimering och indexuttryck.

  • Stöder PostgreSQL "accentokänsliga" sammanställningar?

Men gör det inte till STRICT , vilket skulle inaktivera "function inlining" och försämra prestandan eftersom array_position() är inte STRICT sig. Se:

  • Funktionen körs snabbare utan STRICT modifierare?

Om du behöver fånga fallet är 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';

För Postgres 9.1 använd t_91 uttryck från ovan. Resten gäller oförändrat.

Närbesläktade:

  • Hur avgör man om NULL finns i en array i Postgres?


  1. MySQL-anslutningsfråga med liknande?

  2. Topp 10 bästa praxis i MySQL

  3. Lagra bilder i PostgreSQL

  4. Hur man konverterar PostgreSQL 9.4:s jsonb-typ till flytande