Opérateur IN vs ANY dans PostgreSQL

Réponses:

158

(Ni un "opérateur", INni ANYun "opérateur". Une "construction" ou un "élément de syntaxe".)

Logiquement , citant le manuel :

INéquivaut à = ANY.

Mais il existe deux variantes de syntaxeIN et deux variantes de ANY. Détails:

IN prendre un ensemble équivaut à = ANYprendre un ensemble , comme démontré ici:

Mais la deuxième variante de chacun n'est pas équivalente à l'autre. La deuxième variante de la ANYconstruction prend un tableau (doit être un type de tableau réel), tandis que la deuxième variante de INprend une liste de valeurs séparées par des virgules . Cela conduit à différentes restrictions dans la transmission des valeurs et peut également conduire à différents plans de requête dans des cas particuliers:

ANY est plus polyvalent

La ANYconstruction est beaucoup plus polyvalente, car elle peut être combinée avec divers opérateurs, pas seulement =. Exemple:

SELECT 'foo' LIKE ANY('{FOO,bar,%oo%}');

Pour un grand nombre de valeurs, fournir un ensemble de meilleures échelles pour chacune:

En relation:

Inversion / opposé / exclusion

"Rechercher les lignes où se idtrouve dans le tableau donné":

SELECT * FROM tbl WHERE id = ANY (ARRAY[1, 2]);

Inversion: « Trouver les lignes où idest pas dans le tableau »:

SELECT * FROM tbl WHERE id <> ALL (ARRAY[1, 2]);
SELECT * FROM tbl WHERE id <> ALL ('{1, 2}');  -- equivalent array literal
SELECT * FROM tbl WHERE NOT (id = ANY ('{1, 2}'));

Les trois équivalents. Le premier avec un constructeur de tableau , les deux autres avec un littéral de tableau . Le type de données peut être dérivé du contexte sans ambiguïté. Sinon, une distribution explicite peut être requise, comme '{1,2}'::int[].

Les lignes avec id IS NULLne transmettent aucune de ces expressions. Pour inclure des NULLvaleurs en plus:

SELECT * FROM tbl WHERE (id = ANY ('{1, 2}')) IS NOT TRUE;
Erwin Brandstetter
la source
4
Ce serait bien de préciser explicitement que les résultats des deuxièmes variantes seront toujours les mêmes. Je suis sûr à 99% que c'est effectivement le cas, mais la réponse ne semble pas le dire. Cela signifie que cela SELECT * from mytable where id in (1, 2, 3)donnera toujours les mêmes lignes que SELECT * from mytable where id = ANY('{1, 2, 3}'), même si elles peuvent potentiellement avoir des plans de requête différents.
KPD
1
ANY ne peut pas être combiné avec l' !=opérateur. Je ne pense pas que ce soit documenté, mais ce select * from foo where id != ANY (ARRAY[1, 2])n'est pas la même chose que select * from foo where id NOT IN (1, 2). D'autre part, select * from foo where NOT (id = ANY (ARRAY[1, 2]))fonctionne comme prévu.
qris
1
@qris: ANYpeut être combiné avec l' !=opérateur. Mais il y a plus que cela. J'ai ajouté un chapitre ci-dessus. (Notez que <>c'est l'opérateur en SQL standard - bien qu'il !=soit également accepté dans Postgres.)
Erwin Brandstetter
Comment fonctionne la dernière version qui inclut des NULLvaleurs? Cela WHERE id = ANY (ARRAY[1, 2]) OR id IS NULL;fonctionnerait-il aussi bien?
dvtan le
1
@dvtan: (id = ...) IS NOT TRUEfonctionne car id = ...évalue uniquement TRUEs'il y a une correspondance réelle. Résultats FALSEou NULLpasser notre test. Voir: stackoverflow.com/a/23767625/939860 . Votre expression ajoutée teste autre chose. Ce serait équivalentWHERE id <> ALL (ARRAY[1, 2]) OR id IS NULL;
Erwin Brandstetter le
3

Il y a deux points évidents, ainsi que les points de l'autre réponse:

  • Ils sont exactement équivalents lors de l'utilisation de sous-requêtes:

    SELECT * FROM table
    WHERE column IN(subquery);
    
    SELECT * FROM table
    WHERE column = ANY(subquery);

D'autre part:

  • Seul l' INopérateur autorise une liste simple:

    SELECT * FROM table
    WHERE column IN(… ,  , …);

Présumer qu'ils sont exactement les mêmes m'a surpris à plusieurs reprises lorsque j'ai oublié que ANYcela ne fonctionne pas avec les listes.

Manngo
la source