Postgres NIE w tablicy

101

Używam rodzimego typu tablicy Postgres i próbuję znaleźć rekordy, w których identyfikator nie znajduje się w identyfikatorach odbiorców tablicy.

Mogę znaleźć, gdzie się znajdują:

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

Ale to nie działa:

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

Jaki jest właściwy sposób sprawdzenia tego stanu?

user577808
źródło
nie WHERE 3 NOT IN recipient_idsdziała?
Janus Troelsen
1
Powiązana uwaga: jak dla text[]i int[]tablica:select not(array[1,2,3] @> array[3]);
Steve Peak
3
Porada dla profesjonalistów: jeśli sprawdzasz, czy nullkolumna jest zawarta w tablicy, czy nie, zawsze powie nie. Zajęło mi około 20 minut debugowania kilku metod zawierających elementy, aby dojść do wniosku, że nie można sprawdzić, czy w tablicy znajduje się wartość null
André Pena

Odpowiedzi:

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

Zawsze można negować WHERE (condition)zWHERE NOT (condition)

Frank Farmer
źródło
2
@aschyiel - Możesz chcieć przełączyć się z powrotem na ANYzamiast w INmiarę recipient_idswzrostu listy danych wejściowych: stackoverflow.com/questions/1009706/ ...
derekm
41

Możesz to trochę odwrócić i powiedzieć „3 nie jest równe wszystkim identyfikatorom”:

where 3 != all (recipient_ids)

Z dobrej instrukcji :

9.21.4. ALL (tablica)

expression operator ALL (array expression)

Po prawej stronie znajduje się wyrażenie w nawiasach, które musi zawierać wartość tablicy. Wyrażenie po lewej stronie jest oceniane i porównywane z każdym elementem tablicy przy użyciu podanego operatora , co musi dać wynik logiczny. Wynik ALLjest „prawdziwy”, jeśli wszystkie porównania dają prawdę (w tym przypadek, w którym tablica ma zero elementów). Wynik jest „fałszywy”, jeśli zostanie znaleziony fałszywy wynik.

mu jest za krótkie
źródło
to naprawdę nie wyjaśnia, dlaczego anynie działa w tym przypadku
seanlinsley
Należy to zaakceptować, ponieważ właściwie wyjaśniało powód. PS można również znaleźć anyi allna postgres doc, który mówi: " x <> ANY (a,b,c) jest równoważne x <> a OR <> b OR x <> c". ref: postgresqltutorial.com/postgresql-any postgresqltutorial.com/postgresql-all
Tyler Temp
19

Rozszerzanie ALL/ANYodpowiedzi

Preferuję wszystkie rozwiązania, które wykorzystują alllub anyosiągają wynik, ceniąc sobie dodatkowe uwagi (np. O NULL- ach). Jako kolejne ulepszenie, oto sposób myślenia o tych operatorach.

Można o nich myśleć jako o operatorach zwarć :

  • all(array)przechodzi przez wszystkie wartości w tablicy, porównując każdą z wartością odniesienia przy użyciu podanego operatora. Gdy tylko wyniki porównania wypadną false, proces kończy się błędem, w przeciwnym razie prawdą. (Porównywalne z logiką zwarciową and.)
  • any(array)przechodzi przez wszystkie wartości w tablicy, porównując każdą z wartością odniesienia przy użyciu podanego operatora. Gdy tylko wynik porównania truezakończy się, proces kończy się na prawdzie, w przeciwnym razie na fałszu. (Porównywalne z logiką zwarciową or.)

Dlatego 3 <> any('{1,2,3}')nie daje pożądanego wyniku: proces porównuje 3 z 1 pod względem nierówności, co jest prawdą, i natychmiast zwraca prawdę. Pojedyncza wartość w tablicy różna od 3 wystarczy, aby spełnić cały warunek. 3 na ostatniej pozycji tablicy to prawdop. nigdy nie używany.

3 <> all('{1,2,3}')z drugiej strony upewnia się, że wszystkie wartości nie są równe 3. Przeszedł przez wszystkie porównania, które dały prawdę, aż do elementu, który daje fałsz (ostatni w tym przypadku), aby zwrócić false jako wynik ogólny. Tego właśnie chce PO.

ThomasH
źródło
12

not (3 = any(recipient_ids))?

Markus Mikkolainen
źródło
Dzięki, korzystałem 3 <> ANY(ARRAY[1,2,3,4]). Powinno działać w ten sposób: \
yeyo
12

aktualizacja:

od postgres 9.3,

możesz użyć NOTw tandemie z @> (zawiera operator), aby to osiągnąć.

TO ZNACZY.

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

Kogut
źródło
12

Uważaj na wartości NULL

Obie ALL:

(some_value != ALL(some_array))

Oraz ANY:

NOT (some_value = ANY(some_array))

Działa, o ile some_arraynie jest zerowa. Jeśli tablica może być pusta, to należy ją uwzględnić za pomocą coalesce (), np

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

Lub

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

Z dokumentów :

Jeśli wyrażenie tablicowe daje tablicę null, wynik ANY będzie zerowy

Jeśli wyrażenie tablicowe daje tablicę zerową, wynikiem ALL będzie null

isapir
źródło
3

Zauważ, że operatory ANY / ALL nie będą działać z indeksami tablicowymi. Jeśli chodzi o indeksy:

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

i negatywne:

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

Następnie można utworzyć indeks, taki jak:

CREATE INDEX recipient_ids_idx on tableName USING GIN(recipient_ids)
Jamming James
źródło
W przeciwieństwie do innych odpowiedzi, ta odpowiedź w rzeczywistości wykorzystuje operator nakładania się tablicy PostgreSQL. &&
Ceiling Gecko,
6
To nie zadziała tak, jak napisano. Operatory tablicowe, takie jak && i @>, wymagają, aby oba elementy były tablicami, co nie jest 3. Aby to zadziałało, kwerenda musiałaby być zapisany jako: SELECT COUNT(*) FROM "messages" WHERE ARRAY[3] && recipient_ids.
Dologan