Operator
Dies baut auf @ Daniel's cleverem Operator auf .
Erstellen Sie dabei die Funktions- / Operatorkombination mit polymorphen Typen . Dann funktioniert es für jeden Typ - genau wie das Konstrukt.
Und mach die Funktion 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
);
Eine Schnellsuche mit symbolhound war leer, sodass der Operator <!>
in keinem der Module verwendet zu werden scheint.
Wenn Sie diesen Operator häufig verwenden, können Sie ihn dem Abfrageplaner näher bringen ( wie in einem Kommentar unter losthorse vorgeschlagen ). Für den Anfang können Sie die Klauseln COMMUTATOR
und hinzufügen NEGATOR
, um das Abfrageoptimierungsprogramm zu unterstützen. Ersetzen Sie CREATE OPERATOR
von oben mit diesem:
CREATE OPERATOR <!> (
PROCEDURE = is_distinct_from(anyelement,anyelement),
LEFTARG = anyelement
, RIGHTARG = anyelement
, COMMUTATOR = <!>
, NEGATOR = =!=
);
Und füge hinzu:
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 = <!>
);
Die zusätzlichen Klauseln helfen jedoch nicht im vorliegenden Anwendungsfall, und einfache Indizes werden weiterhin nicht verwendet. Um dies zu erreichen, ist es viel ausgefeilter. (Ich habe es nicht versucht.) Weitere Informationen finden Sie im Kapitel "Informationen zur Bedieneroptimierung" im Handbuch.
Testfall
Der Testfall in der Frage kann nur erfolgreich sein, wenn alle Werte im Array identisch sind. Für das Array in der Frage ( '{null,A}'::text[]
) ist das Ergebnis immer WAHR. Ist das beabsichtigt? Ich habe einen weiteren Test für "IS DISTINCT FROM ALL" hinzugefügt:
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 mit Standardbetreibern
foo IS DISTINCT FROM ANY (test_arr) -- illegal syntax
kann fast übersetzt werden
foo = ALL (test_arr) IS NOT TRUE
foo = ALL (test_arr)
ergibt ...
TRUE
..wenn alle Elemente sind foo
FALSE
..wenn irgendein NOT NULL
Element ist <> foo
NULL
..wenn mindestens ein Element IS NULL
und kein Element ist<> foo
Der verbleibende Eckfall ist also wo
- foo IS NULL
- und test_arr
besteht nur aus NULL
Elementen.
Wenn einer von beiden ausgeschlossen werden kann, sind wir fertig. Verwenden Sie daher den einfachen Test, wenn
- die Spalte definiert ist NOT NULL
.
- oder Sie wissen, dass das Array niemals alle NULL-Werte enthält.
Sonst zusätzlich testen:
AND ('A' = ALL(test_arr) IS NOT NULL OR
'B' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Wo 'A'
und 'B'
kann keine unterschiedlichen Werte. Erklärung und Alternativen unter dieser verwandten Frage zu SO:
Ist Array alle NULLs in PostgreSQL
Noch einmal, wenn Sie wissen , über einen beliebigen Wert, der in nicht existieren kann test_arr
zum Beispiel die leere Zeichenkette ''
, können Sie noch vereinfachen:
AND ('' = ALL(test_arr) IS NOT NULL OR
foo IS NOT NULL)
Hier ist eine vollständige Testmatrix, um nach allen Kombinationen zu suchen:
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
Dies ist etwas ausführlicher als Andriys EXCEPT
Lösung , aber wesentlich schneller.