Opérateur
Cela s'appuie sur l'opérateur intelligent de @ Daniel .
Tout en y étant, créez le combo fonction / opérateur en utilisant des types polymorphes . Ensuite, cela fonctionne pour tout type - tout comme la construction.
Et faites la fonction 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
);
Une recherche rapide avec symbolhound est apparue vide, de sorte que l'opérateur <!>
ne semble pas être utilisé dans aucun des modules.
Si vous utilisez beaucoup cet opérateur, vous pouvez l'étoffer davantage pour aider le planificateur de requêtes ( comme losthorse l'a suggéré dans un commentaire ). Pour commencer, vous pouvez ajouter les clauses COMMUTATOR
et NEGATOR
pour aider l'optimiseur de requête. Remplacez CREATE OPERATOR
par le haut par ceci:
CREATE OPERATOR <!> (
PROCEDURE = is_distinct_from(anyelement,anyelement),
LEFTARG = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = <!>
, NEGATOR = =!=
);
Et ajouter:
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 = <!>
);
Mais les clauses supplémentaires n'aideront pas avec le cas d'utilisation à portée de main et les index simples ne seront toujours pas utilisés. C'est beaucoup plus sophistiqué pour y parvenir. (Je n'ai pas essayé.) Lisez le chapitre "Informations sur l'optimisation de l'opérateur" dans le manuel pour plus de détails.
Cas de test
Le scénario de test dans la question ne peut réussir que si toutes les valeurs du tableau sont identiques. Pour le tableau dans la question ( '{null,A}'::text[]
), le résultat est toujours VRAI. Est-ce prévu? J'ai ajouté un autre test pour "EST DISTINCT DE TOUS":
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
Alternative avec opérateurs standard
foo IS DISTINCT FROM ANY (test_arr) -- illegal syntax
peut presque être traduit en
foo = ALL (test_arr) IS NOT TRUE
foo = ALL (test_arr)
rendements ...
TRUE
.. si tous les éléments sont foo
FALSE
.. si un NOT NULL
élément est <> foo
NULL
.. si au moins un élément IS NULL
et aucun élément n'est<> foo
Ainsi, le cas d'angle restant est où
- foo IS NULL
- et ne se test_arr
compose que d' NULL
éléments.
Si l'un ou l'autre peut être exclu, nous avons terminé. Par conséquent, utilisez le test simple si
- la colonne est définie NOT NULL
.
- ou vous savez que le tableau n'est jamais entièrement NULL.
Sinon, testez en plus:
AND ('A' = ALL(test_arr) IS NOT NULL OR
'B' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Où 'A'
et 'B'
peut être des valeurs distinctes. Explication et alternatives sous cette question connexe sur SO: le
tableau est-il tous NULL dans PostgreSQL
Encore une fois, si vous connaissez une valeur qui ne peut pas exister dans test_arr
, par exemple la chaîne vide ''
, vous pouvez toujours simplifier:
AND ('' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Voici une matrice de test complète pour vérifier toutes les combinaisons:
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
C'est un peu plus verbeux que la EXCEPT
solution d' Andriy , mais c'est beaucoup plus rapide.