Operatore
Questo si basa sull'abile operatore di Daniel .
Mentre ci sei, crea la combo funzione / operatore usando tipi polimorfici . Quindi funziona per qualsiasi tipo, proprio come il costrutto.
E rendere la funzione 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
);
Una rapida ricerca con symbolhound è risultata vuota, quindi l'operatore <!>
non sembra essere in uso in nessuno dei moduli.
Se hai intenzione di utilizzare questo operatore molto, potresti approfondire un po 'di più per aiutare il pianificatore di query ( come suggerito in un commento LostHorse ). Per cominciare, è possibile aggiungere le clausole COMMUTATOR
e NEGATOR
per aiutare Query Optimizer. Sostituisci CREATE OPERATOR
dall'alto con questo:
CREATE OPERATOR <!> (
PROCEDURE = is_distinct_from(anyelement,anyelement),
LEFTARG = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = <!>
, NEGATOR = =!=
);
E aggiungi:
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 = <!>
);
Ma le clausole aggiuntive non aiuteranno il caso d'uso a portata di mano e gli indici semplici non saranno ancora utilizzati. È molto più sofisticato per raggiungere questo obiettivo. (Non ho provato.) Leggi il capitolo "Informazioni sull'ottimizzazione dell'operatore" nel manuale per i dettagli.
Caso di prova
Il caso di test nella domanda può avere esito positivo solo se tutti i valori nella matrice sono identici. Per l'array nella domanda ( '{null,A}'::text[]
) il risultato è sempre VERO. È previsto? Ho aggiunto un altro test per "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 con operatori standard
foo IS DISTINCT FROM ANY (test_arr) -- illegal syntax
può quasi essere tradotto in
foo = ALL (test_arr) IS NOT TRUE
foo = ALL (test_arr)
rese ...
TRUE
.. se tutti gli elementi sono foo
FALSE
.. se qualche NOT NULL
elemento è <> foo
NULL
.. se almeno uno IS NULL
e nessun elemento lo sono<> foo
Quindi, il rimanente caso d'angolo è dove
- foo IS NULL
- e test_arr
consiste solo di NULL
elementi.
Se uno dei due può essere escluso, abbiamo finito. Pertanto, utilizzare il test semplice se
- la colonna è definita NOT NULL
.
- oppure si conosce la matrice non è mai tutta vuota.
Altrimenti, prova anche:
AND ('A' = ALL(test_arr) IS NOT NULL OR
'B' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Dove 'A'
e 'B'
può essere qualsiasi valori distinti. Spiegazione e alternative sotto questa domanda correlata su SO:
array è tutto NULL in PostgreSQL
Ancora una volta, se conosci qualche valore che non può esistere test_arr
, ad esempio la stringa vuota ''
, puoi comunque semplificare:
AND ('' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Ecco una matrice di test completa per verificare tutte le combinazioni:
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
Questo è un po 'più dettagliato della EXCEPT
soluzione di Andriy , ma è sostanzialmente più veloce.