271

What is the difference between IN and ANY operator in PostgreSQL?
The working mechanism of both seems to be the same. Can anyone explain this with an example?

mohangraj
  • 9,842
  • 19
  • 59
  • 94
  • 4
    Possible duplicate of [postgreSQL - in vs any](http://stackoverflow.com/questions/30263671/postgresql-in-vs-any) – Vivek S. Jan 06 '16 at 06:47
  • Does this answer your question? [Difference between in and any operators in sql](https://stackoverflow.com/questions/3699356/difference-between-in-and-any-operators-in-sql) – philipxy Feb 16 '20 at 11:06

3 Answers3

387

(Strictly speaking, IN and ANY are Postgres "constructs" or "syntax elements", rather than "operators".)

Logically, quoting the manual:

IN is equivalent to = ANY.

But there are two syntax variants of IN and two variants of ANY. Details:

IN taking a set is equivalent to = ANY taking a set, as demonstrated here:

But the second variant of each is subtly different. The second variant of the ANY construct takes an array (must be an actual array type), while the second variant of IN takes a comma-separated list of values. This leads to different restrictions in passing values and can also lead to different query plans in special cases:

ANY is more versatile

The ANY construct is far more versatile, as it can be combined with various operators, not just =. Example:

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

For a big number of values, providing a set scales better for each:

Related:

Inversion / opposite / exclusion

"Find rows where id is in the given array":

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

Inversion: "Find rows where id is not in the array":

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}'));

All three equivalent. The first with ARRAY constructor, the other two with array literal. The type of the untyped array literal is derived from (known) element type to the left.
In other constellations (typed array value / you want a different type / ARRAY constructor for a non-default type) you may need to cast explicitly.

Rows with id IS NULL do not pass either of these expressions. To include NULL values additionally:

SELECT * FROM tbl WHERE (id = ANY ('{1, 2}')) IS NOT TRUE;
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • 7
    It'd be nice to explicitly clarify that the results of the second variants will always be the same. I'm 99% sure that is in fact the case but the answer doesn't seem to state it. Meaning that `SELECT * from mytable where id in (1, 2, 3)` will always result in the same rows as `SELECT * from mytable where id = ANY('{1, 2, 3}')`, even if they potentially might have different query plans. – KPD Apr 08 '18 at 23:44
  • 2
    `ANY` **cannot** be combined with the `!=` operator. I don't think it's documented, but `select * from foo where id != ANY (ARRAY[1, 2])` is not the same as `select * from foo where id NOT IN (1, 2)`. On the other hand, `select * from foo where NOT (id = ANY (ARRAY[1, 2]))` works as expected. – qris Dec 07 '18 at 12:36
  • 1
    @qris: `ANY` can be combined with the `!=` operator. But there is more to it. I added a chapter above. (Note that `<>` is the operator in standard SQL - though `!=` is accepted as well in Postgres.) – Erwin Brandstetter Feb 07 '19 at 12:24
  • How does the last version that includes `NULL` values work? Would `WHERE id = ANY (ARRAY[1, 2]) OR id IS NULL;` work just as well? – davidtgq Feb 01 '20 at 04:30
  • 2
    @dvtan: `(id = ...) IS NOT TRUE` works because `id = ...` only evaluates to `TRUE` if there is an actual match. Outcomes `FALSE` or `NULL` pass our test. See: https://stackoverflow.com/a/23767625/939860. Your added expression tests for something else. This would be equivalent `WHERE id <> ALL (ARRAY[1, 2]) OR id IS NULL;` – Erwin Brandstetter Feb 03 '20 at 23:17
  • It's said that index is not used with `=ANY('{1, 2, 3}')`, but I just tried to do both `WHERE key = ANY ('{1, 3, 5, ...49}')` as well as `WHERE key IN (1, 3, 5, ...49)` on a big table and the `EXPLAIN` output is absolutely the same and it uses index scan. Even more, it says `=ANY` in the explain output even for `IN`. I'm on PostgreSQL 12.4 – seriyPS Jul 23 '21 at 09:20
  • @seriyPS: `It's said that index is not used ...`? I am not saying anything of the sort. If you mean "Index not used with =any() but used with in", that's just the title of a related question - linking to my related answer. – Erwin Brandstetter Jul 25 '21 at 22:52
  • @erwin-brandstetter ah, ok, sorry. But I think others may be confused the same way as I did, so I'll leave the comment where it is. – seriyPS Jul 27 '21 at 07:41
  • 1
    @ErwinBrandstetter I found out this commit patched. not sure how faster can it be to speed IN clause. https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=50e17ad28 – jian Jul 06 '23 at 11:26
  • @jian: That should help. But the release notes only mention a related improvement for `NOT IN`. I'll have to test. But not now ... – Erwin Brandstetter Jul 09 '23 at 21:47
15

There are two obvious points, as well as the points in the other answer:

  • They are exactly equivalent when using sub queries:

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

On the other hand:

  • Only the IN operator allows a simple list:

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

Presuming they are exactly the same has caught me out several times when forgetting that ANY doesn’t work with lists.

Manngo
  • 14,066
  • 10
  • 88
  • 110
1

'in' is syntaxis sugar, you can take a look to plan analyse and will see that 'in' will be transform to =ANY('...,...')::yourType[]