Postgres NICHT im Array

92

Ich verwende den nativen Array-Typ von Postgres und versuche, die Datensätze zu finden, deren ID nicht in den Empfänger-IDs des Arrays enthalten ist.

Ich kann finden, wo sie IN sind:

SELECT COUNT(*) FROM messages WHERE (3 = ANY (recipient_ids))

Das funktioniert aber nicht:

SELECT COUNT(*) FROM messages WHERE (3 != ANY (recipient_ids))
SELECT COUNT(*) FROM messages WHERE (3  = NOT ANY (recipient_ids))

Was ist der richtige Weg, um auf diesen Zustand zu testen?

user577808
quelle
funktioniert WHERE 3 NOT IN recipient_ids?
Janus Troelsen
1
Verwandte Anmerkung: wie für text[]und int[]Array:select not(array[1,2,3] @> array[3]);
Steve Peak
3
Pro-Tipp: Wenn Sie überprüfen, ob eine nullSpalte in einem Array enthalten ist oder nicht, wird immer Nein gesagt. Ich habe ungefähr 20 Minuten gebraucht, um mehrere enthaltene Methoden zu debuggen, um zu dem Schluss zu kommen, dass man nicht überprüfen kann, ob null in einem Array enthalten ist
André Pena

Antworten:

131
SELECT COUNT(*) FROM "messages" WHERE NOT (3 = ANY (recipient_ids))

Sie können immer negieren WHERE (condition)mitWHERE NOT (condition)

Frank Farmer
quelle
2
@aschyiel - Vielleicht möchten Sie zurück zu ANYanstatt wechseln, INwenn Ihre recipient_idsEingabeliste wächst: stackoverflow.com/questions/1009706/…
derekm
39

Sie könnten es ein wenig umdrehen und sagen "3 ist nicht gleich allen IDs":

where 3 != all (recipient_ids)

Aus dem feinen Handbuch :

9.21.4. ALL (Array)

expression operator ALL (array expression)

Die rechte Seite ist ein Ausdruck in Klammern, der einen Array-Wert ergeben muss. Der Ausdruck auf der linken Seite wird ausgewertet und mit jedem Element des Arrays unter Verwendung des angegebenen Operators verglichen , der ein boolesches Ergebnis liefern muss. Das Ergebnis von ALList "true", wenn alle Vergleiche true ergeben (einschließlich des Falls, in dem das Array keine Elemente enthält). Das Ergebnis ist "falsch", wenn ein falsches Ergebnis gefunden wird.

mu ist zu kurz
quelle
das erklärt nicht wirklich, warum anyin diesem Fall nicht funktioniert
Seanlinsley
Dies sollte akzeptiert werden, da es den Grund richtig erklärte. PS finden Sie auch anyund allauf postgres doc, das sagt: " x <> ANY (a,b,c) ist gleichbedeutend mit x <> a OR <> b OR x <> c". ref: postgresqltutorial.com/postgresql-any postgresqltutorial.com/postgresql-all
Tyler Temp
17

Augmentierenden die ALL/ANYAntworten

Ich bevorzuge alle Lösungen, die das Ergebnis verwenden alloder verwenden any, um die zusätzlichen Hinweise zu schätzen (z. B. über NULL s). Als weitere Erweiterung finden Sie hier eine Möglichkeit, über diese Operatoren nachzudenken.

Sie können sie als Kurzschlussbetreiber betrachten :

  • all(array)Durchläuft alle Werte im Array und vergleicht sie mit dem angegebenen Wert mit dem angegebenen Operator. Sobald ein Vergleich ergibt false, endet der Prozess mit false, andernfalls true. (Vergleichbar mit logischem Kurzschluss and.)
  • any(array)Durchläuft alle Werte im Array und vergleicht sie mit dem angegebenen Wert mit dem angegebenen Operator. Sobald ein Vergleich ergibt true, endet der Prozess mit wahr, andernfalls falsch. (Vergleichbar mit logischem Kurzschluss or.)

Aus diesem Grund 3 <> any('{1,2,3}')wird nicht das gewünschte Ergebnis erzielt: Der Prozess vergleicht 3 mit 1 für die Ungleichung, was wahr ist, und gibt sofort wahr zurück. Ein einzelner Wert im Array, der sich von 3 unterscheidet, reicht aus, um die gesamte Bedingung zu erfüllen. Die 3 an der letzten Array-Position ist prob. nie benutzt.

3 <> all('{1,2,3}')Auf der anderen Seite wird sichergestellt, dass nicht alle Werte gleich 3 sind. Es werden alle Vergleiche durchlaufen, die true ergeben, bis zu einem Element, das false ergibt (in diesem Fall das letzte), um false als Gesamtergebnis zurückzugeben. Das will das OP.

ThomasH
quelle
12

not (3 = any(recipient_ids))?

Markus Mikkolainen
quelle
Danke, ich habe benutzt 3 <> ANY(ARRAY[1,2,3,4]). Es hätte so funktionieren sollen: \
yeyo
11

ein Update:

ab postgres 9.3,

Sie können dies auch NOTzusammen mit dem @> Operator (enthält) verwenden .

IE.

SELECT COUNT(*) FROM "messages" WHERE NOT recipient_ids @> ARRAY[3];

Hahn
quelle
10

Vorsicht vor NULL

Beide ALL:

(some_value != ALL(some_array))

Und ANY:

NOT (some_value = ANY(some_array))

Würde funktionieren, solange some_arraynicht null ist. Wenn das Array null sein könnte, müssen Sie es mit coalesce () berücksichtigen, z

(some_value != ALL(coalesce(some_array, array[]::int[])))

Oder

NOT (some_value = ANY(coalesce(some_array, array[]::int[])))

Aus den Dokumenten :

Wenn der Array-Ausdruck ein Null-Array ergibt, ist das Ergebnis von ANY null

Wenn der Array-Ausdruck ein Null-Array ergibt, ist das Ergebnis von ALL null

Isapir
quelle
3

Beachten Sie, dass die ANY / ALL-Operatoren nicht mit Array-Indizes funktionieren. Wenn Indizes im Auge behalten:

SELECT COUNT(*) FROM "messages" WHERE 3 && recipient_ids

und das Negative:

SELECT COUNT(*) FROM "messages" WHERE NOT (3 && recipient_ids)

Ein Index kann dann wie folgt erstellt werden:

CREATE INDEX recipient_ids_idx on tableName USING GIN(recipient_ids)
James jammen
quelle
Im Gegensatz zu anderen Antworten verwendet diese Antwort tatsächlich den PostgreSQL-Array-Überlappungsoperator. &&
Decke Gecko
6
Dies funktioniert nicht wie geschrieben. Array-Operatoren wie && und @> erfordern, dass beide Elemente Arrays sind, was 3 nicht ist. Damit dies funktioniert, muss die Abfrage wie folgt geschrieben werden : SELECT COUNT(*) FROM "messages" WHERE ARRAY[3] && recipient_ids.
Dologan