Operador
Isso se baseia no operador inteligente de @ Daniel .
Enquanto estiver nisso, crie a combinação função / operador usando tipos polimórficos . Então funciona para qualquer tipo - assim como a construção.
E faça a função IMMUTABLE
.
CREATE FUNCTION is_distinct_from(anyelement, anyelement)
RETURNS bool LANGUAGE sql IMMUTABLE AS
'SELECT $1 IS DISTINCT FROM $2';
CREATE OPERATOR <!> (
PROCEDURE = is_distinct_from(anyelement,anyelement),
LEFTARG = anyelement
, RIGHTARG = anyelement
);
Uma pesquisa rápida com o symbolhound apareceu vazia, para que o operador <!>
não pareça estar em uso em nenhum dos módulos.
Se você for usar muito esse operador, poderá aprimorá-lo um pouco mais para ajudar o planejador de consultas ( como o último sugerido em um comentário ). Para iniciantes, você pode adicionar as cláusulas COMMUTATOR
e NEGATOR
para ajudar o otimizador de consultas. Substitua CREATE OPERATOR
de cima por este:
CREATE OPERATOR <!> (
PROCEDURE = is_distinct_from(anyelement,anyelement),
LEFTARG = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = <!>
, NEGATOR = =!=
);
E adicione:
CREATE FUNCTION is_not_distinct_from(anyelement, anyelement)
RETURNS bool LANGUAGE sql IMMUTABLE AS
'SELECT $1 IS NOT DISTINCT FROM $2';
CREATE OPERATOR =!= (
PROCEDURE = is_not_distinct_from(anyelement,anyelement),
LEFTARG = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = =!=
, NEGATOR = <!>
);
Mas as cláusulas adicionais não ajudarão no caso de uso em questão e índices simples ainda não serão usados. É muito mais sofisticado conseguir isso. (Não tentei.) Leia o capítulo "Informações sobre otimização do operador" no manual para obter detalhes.
Caso de teste
O caso de teste na pergunta só poderá ter sucesso se todos os valores na matriz forem idênticos. Para a matriz na questão ( '{null,A}'::text[]
), o resultado é sempre VERDADEIRO. Isso é pretendido? Adicionei outro teste para "IS DISTINCT FROM ALL":
SELECT foo
, foo <!> ANY ('{null,A}'::text[]) AS chk_any
, foo <!> ALL ('{null,A}'::text[]) AS chk_all
FROM (
VALUES ('A'),('Z'),(NULL)
) z(foo)
foo | chk_any | chk_all
-----+---------+---------
A | t | f
Z | t | t
| t | f
Alternativa com operadores padrão
foo IS DISTINCT FROM ANY (test_arr) -- illegal syntax
quase pode ser traduzido para
foo = ALL (test_arr) IS NOT TRUE
foo = ALL (test_arr)
rendimentos ...
TRUE
.. se todos os elementos forem foo
FALSE
.. se algum NOT NULL
elemento for <> foo
NULL
.. se pelo menos um elemento IS NULL
e nenhum elemento for<> foo
Portanto, a caixa de canto restante é onde
- foo IS NULL
- e test_arr
consiste em nada além de NULL
elementos.
Se qualquer um deles puder ser descartado, estamos prontos. Portanto, use o teste simples se
- a coluna estiver definida NOT NULL
.
- ou você sabe que a matriz nunca é todos os NULLs.
Senão, teste adicionalmente:
AND ('A' = ALL(test_arr) IS NOT NULL OR
'B' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Onde 'A'
e 'B'
podem ser quaisquer valores distintos. Explicação e alternativas sob esta pergunta relacionada no SO:
A matriz é todos os NULLs no PostgreSQL
Novamente, se você souber de algum valor que não possa existir test_arr
, por exemplo, a sequência vazia ''
, ainda poderá simplificar:
AND ('' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Aqui está uma matriz de teste completa para verificar todas as combinações:
SELECT foo, test_arr
, foo = ALL(test_arr) IS NOT TRUE AS test_simple
, foo = ALL(test_arr) IS NOT TRUE
AND ('A' = ALL(test_arr) IS NOT NULL OR
'B' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL) AS test_sure
FROM (
VALUES ('A'),('Z'),(NULL)
) v(foo)
CROSS JOIN (
VALUES ('{null,A}'::text[]),('{A,A}'),('{null,null}')
) t(test_arr)
foo | test_arr | test_simple | test_sure
-----+-------------+-------------+-----------
A | {NULL,A} | t | t
A | {A,A} | f | f -- only TRUE case
A | {NULL,NULL} | t | t
Z | {NULL,A} | t | t
Z | {A,A} | t | t
Z | {NULL,NULL} | t | t
| {NULL,A} | t | t
| {A,A} | t | t
| {NULL,NULL} | t | f -- special case
Isso é um pouco mais detalhado que a EXCEPT
solução de Andriy , mas é substancialmente mais rápido.