Postgres PAS dans le tableau

96

J'utilise le type de tableau natif de Postgres et j'essaie de trouver les enregistrements où l'ID n'est pas dans les ID de destinataire du tableau.

Je peux trouver où ils sont DANS:

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

Mais cela ne fonctionne pas:

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

Quelle est la bonne façon de tester cette condition?

user577808
la source
ça WHERE 3 NOT IN recipient_idsmarche?
Janus Troelsen
1
Note connexe: comme pour text[]et int[]tableau:select not(array[1,2,3] @> array[3]);
Steve Peak
3
Conseil de pro: Si vous vérifiez si une nullcolonne est contenue ou non dans un tableau, elle dira toujours non. Il m'a fallu environ 20 minutes de débogage de plusieurs méthodes contenant pour arriver à la conclusion que vous ne pouvez pas vérifier si null est contenu dans un tableau
André Pena

Réponses:

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

Vous pouvez toujours nier WHERE (condition)avecWHERE NOT (condition)

Frank Farmer
la source
2
@aschyiel - Vous voudrez peut-être revenir à ANYau lieu de au INfur et à mesure que votre recipient_idsliste d'entrées s'allonge: stackoverflow.com/questions/1009706/…
derekm
39

Vous pouvez le retourner un peu et dire "3 n'est pas égal à tous les ID":

where 3 != all (recipient_ids)

Du manuel fin :

9.21.4. TOUS (tableau)

expression operator ALL (array expression)

Le côté droit est une expression entre parenthèses, qui doit produire une valeur de tableau. L'expression de gauche est évaluée et comparée à chaque élément du tableau à l'aide de l' opérateur donné , qui doit donner un résultat booléen. Le résultat de ALLest "vrai" si toutes les comparaisons donnent vrai (y compris le cas où le tableau ne contient aucun élément). Le résultat est "faux" si un résultat faux est trouvé.

mu est trop court
la source
cela n'explique pas vraiment pourquoi anyne fonctionne pas dans ce cas
seanlinsley
Cela doit être accepté car il explique correctement la raison. PS vous pouvez également trouver anyet allsur postgres doc, qui dit: " x <> ANY (a,b,c) équivaut à x <> a OR <> b OR x <> c". ref: postgresqltutorial.com/postgresql-any postgresqltutorial.com/postgresql-all
Tyler Temp
19

Augmenter les ALL/ANYréponses

Je préfère toutes les solutions qui utilisent allou anypour obtenir le résultat, en appréciant les notes supplémentaires (par exemple à propos des NULL ). Comme autre augmentation, voici une façon de penser à ces opérateurs.

Vous pouvez les considérer comme des opérateurs de court-circuit :

  • all(array)parcourt toutes les valeurs du tableau, en les comparant à la valeur de référence à l'aide de l'opérateur fourni. Dès qu'une comparaison aboutit false, le processus se termine par false, sinon true. (Comparable à la logique de court-circuit and.)
  • any(array)parcourt toutes les valeurs du tableau, en les comparant à la valeur de référence à l'aide de l'opérateur fourni. Dès qu'une comparaison aboutit true, le processus se termine par vrai, sinon faux. (Comparable à la logique de court-circuit or.)

C'est pourquoi 3 <> any('{1,2,3}')ne donne pas le résultat souhaité: le processus compare 3 à 1 pour l'inégalité, ce qui est vrai, et renvoie immédiatement vrai. Une seule valeur du tableau différente de 3 suffit pour que la condition entière soit vraie. Le 3 dans la dernière position du tableau est prob. jamais utilisé.

3 <> all('{1,2,3}')d'autre part s'assure que toutes les valeurs ne sont pas égales à 3. Il exécutera toutes les comparaisons qui donnent vrai jusqu'à un élément qui donne faux (le dernier dans ce cas), pour retourner faux comme résultat global. C'est ce que veut l'OP.

ThomasH
la source
12

not (3 = any(recipient_ids))?

Markus Mikkolainen
la source
Merci, j'utilisais 3 <> ANY(ARRAY[1,2,3,4]). Cela aurait dû fonctionner de cette façon: \
yeyo
11

une mise à jour:

à partir de postgres 9.3,

vous pouvez également utiliser NOTen tandem avec l' @> opérateur (contains) pour y parvenir.

C'EST À DIRE.

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

Coq
la source
11

Méfiez-vous des NULL

Les deux ALL:

(some_value != ALL(some_array))

Et ANY:

NOT (some_value = ANY(some_array))

Cela fonctionnerait tant que ce some_arrayn'est pas nul Si le tableau peut être nul, vous devez en tenir compte avec coalesce (), par exemple

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

Ou

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

À partir de la documentation :

Si l'expression de tableau donne un tableau nul, le résultat de ANY sera nul

Si l'expression de tableau donne un tableau nul, le résultat de ALL sera nul

isapir
la source
3

Notez que les opérateurs ANY / ALL ne fonctionneront pas avec les index de tableau. Si vous pensez aux index:

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

et le négatif:

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

Un index peut alors être créé comme:

CREATE INDEX recipient_ids_idx on tableName USING GIN(recipient_ids)
brouillage james
la source
Contrairement à d'autres réponses, cette réponse utilise en fait l'opérateur de chevauchement de tableau PostgreSQL. &&
Ceiling Gecko le
6
Cela ne fonctionnera pas comme écrit. Les opérateurs de tableau comme && et @> exigent que les deux éléments soient des tableaux, ce qui n'est pas le cas 3. Pour que cela fonctionne, la requête doit être écrite comme suit SELECT COUNT(*) FROM "messages" WHERE ARRAY[3] && recipient_ids:.
Dologan le