PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Verifique se NULL existe no array Postgres

Postgres 9.5 ou posterior


Ou use array_position() . Basicamente:
SELECT array_position(arr, NULL) IS NOT NULL AS array_has_null

Veja demonstração abaixo.

Postgres 9.3 ou posterior


Você pode testar com as funções internas array_remove() ou array_replace() .

Postgres 9.1 ou qualquer versão


Se você saber um único elemento que nunca pode existir em seus arrays, você pode usar isso rápido expressão. Digamos que você tenha uma matriz de números positivos e -1 nunca pode estar nele:
-1 = ANY(arr) IS NULL

Resposta relacionada com explicação detalhada:
  • É array todos os NULLs no PostgreSQL

Se você não puder ter certeza absoluta , você poderia voltar para um dos caros, mas seguros métodos com unnest() . Como:
(SELECT bool_or(x IS NULL) FROM unnest(arr) x)

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

Mas você pode ter rápido e seguro com um CASE expressão. Use um número improvável e volte para o método seguro se ele existir. Você pode querer tratar o caso arr IS NULL separadamente. Veja demonstração abaixo.

Demonstração

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

Resultado:
 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

Observe que array_remove() e array_position() não são permitidos para matrizes multidimensionais . Todas as expressões à direita de t_93a só funcionam para matrizes unidimensionais.

db<>mexa aqui - Postgres 13, com mais testes
Antigo sqlfiddle

Configuração do comparativo de mercado


Os tempos adicionados são de um teste comparativo com 200 mil linhas no Postgres 9.5 . Esta é a minha configuração:
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;

Invólucro de função


Para uso repetido , eu criaria uma função no Postgres 9.5 assim:
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 apenas para Postgres 9.6 ou posterior.

Usando um tipo de entrada polimórfico, isso funciona para qualquer tipo de array, não apenas int[] .

Torne-o IMMUTABLE para permitir otimização de desempenho e expressões de índice.
  • O PostgreSQL suporta agrupamentos "insensíveis ao acento"?

Mas não torne isso STRICT , o que desabilitaria o "inlining da função" e prejudicaria o desempenho porque array_position() não é STRICT em si. Ver:
  • A função é executada mais rapidamente sem o modificador STRICT?

Se você precisar pegar o caso 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';

Para Postgres 9.1 use o t_91 expressão de cima. O resto aplica-se inalterado.

Intimamente relacionado:
  • Como determinar se NULL está contido em um array no Postgres?