294

Using Postgres 9.0, I need a way to test if a value exists in a given array. So far I came up with something like this:

select '{1,2,3}'::int[] @> (ARRAY[]::int[] || value_variable::int)

But I keep thinking there should be a simpler way to this, I just can't see it. This seems better:

select '{1,2,3}'::int[] @> ARRAY[value_variable::int]

I believe it will suffice. But if you have other ways to do it, please share!

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Mike Starov
  • 7,000
  • 7
  • 36
  • 37

8 Answers8

453

Simpler with the ANY construct:

SELECT value_variable = ANY ('{1,2,3}'::int[])

The right operand of ANY (between parentheses) can either be a set (result of a subquery, for instance) or an array. There are several ways to use it:

Important difference: Array operators (<@, @>, && et al.) expect array types as operands and support GIN or GiST indices in the standard distribution of PostgreSQL, while the ANY construct expects an element type as left operand and can be supported with a plain B-tree index (with the indexed expression to the left of the operator, not the other way round like it seems to be in your example). Example:

None of this works for NULL elements. To test for NULL:

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • Thanks. Must have skipped that part of manual. This works great. It has a side effect of automatic casting. Ex: SELECT 1::smallint = ANY ('{1,2,3}'::int[]) works. Just make sure to put ANY() on the right side of expression. – Mike Starov Jun 27 '12 at 23:52
  • Thanks for the answer. Got a problem where my query worked on local, but in heroku was throwing this message `ANY/ALL (array) requires array on right side`, the add of `::int[]` did the charm. – kinduff Dec 13 '12 at 19:14
  • where S.employee_id <@ ANY ('"+employeeIDsArray+"'::int[]) This returns PSQLException: ERROR: missing dimension value – Ramprasad Mar 29 '14 at 13:11
  • @Ramprasad: `s.employee_id <@ '+list_of_employeeIDs+'::int[]` - no `ANY` with the `<@` operator. – Erwin Brandstetter Mar 29 '14 at 13:18
  • @ErwinBrandstetter Brandstetter Again same error occurs.Here list_of_employeeIDs means list or array? I try to pass scala array variable – Ramprasad Mar 31 '14 at 04:55
  • @Ramprasad: List means list. It is *cast* to an array in the example. What I did not make clear: for the `<@` operator both sides have to be an array: `ARRAY[s.employee_id] <@ '+list_of_employeeIDs+'::int[]` [Array syntax examples.](http://stackoverflow.com/questions/7681622/issue-for-insert-using-psycopg/7681837#7681837) – Erwin Brandstetter Mar 31 '14 at 12:29
  • In my case s.employee_id is not an array it is single int value.I want to check this single employee_id exists in employeeIDsArray – Ramprasad Mar 31 '14 at 12:47
  • @Ramprasad: So *make* it an array. If that's still unclear, please ask a *new question* (not comment). – Erwin Brandstetter Mar 31 '14 at 12:50
  • error syntax in `SELECT 1 = ANY array[1,2]` but not in `SELECT 1 = ANY (array[1,2])`, why?? The manual say nothing (!). – Peter Krauss Mar 31 '14 at 13:29
  • @PeterKrauss: Because `ANY` requires parentheses, like demonstrated in my answer. Follow the link to the manual for details. It *does* show parentheses. – Erwin Brandstetter Mar 31 '14 at 13:31
  • Sorry (!), yes... Well, I not understand this PostgreSQL behaviour where `(expression)` *is not the same as* `expression`, there are a guide for "parentheses rules in PostgreSQL"? – Peter Krauss Mar 31 '14 at 13:40
  • @PeterKrauss: The expression does not require parentheses. `ANY` does. That's all. – Erwin Brandstetter Mar 31 '14 at 14:03
  • ... OK, sorry, is a "personal indignation", with the lack of logic/coherence or a lack of "manual lighting exceptions" (for the usual parenthesis rules)... [Now I am expressing it as a question](http://stackoverflow.com/q/22763940/287948) – Peter Krauss Mar 31 '14 at 14:43
  • 9
    Although this is a dinosaur question in internet years, slow folks like me should be made aware that `'something' = ANY(some_array)` can also be used in a `WHERE` clause. For reasons known only to Crom, I have spent the last four years thinking that I couldn't use array comparators in `WHERE` clauses. Those days are gone now. (I was dropped on my head as a child, so maybe it's just me). – GT. Aug 05 '16 at 07:35
  • 3
    @GT.: The gist of it: *any* `boolean` expression works in the `WHERE` clause - Crom willing. – Erwin Brandstetter Aug 05 '16 at 16:19
  • I am trying to do something similar but I am struggling to get the actual array from a value in a table. Defining the array manually woks fine. `IF 'Hello' = ANY (ARRAY['Hello', 'World']::text[]) THEN ...` works but `IF 'Hello' = ANY (SELECT list FROM public."HelloWorldTable" WHERE name = 'HelloWorldList') THEN ...` raises an error. Also tried adding `::text` and `::text[]` but can't get it working. Please help. – Kajsa Dec 10 '19 at 14:36
  • @Kajsa: I get what you are trying to do. But with a *subselect*, `ANY` expects a *set*, not an array. Use instead: `IF EXISTS (SELECT FROM public."HelloWorldTable" WHERE name = 'HelloWorldList' AND 'Hello' = ANY (list)) THEN ...`. See: https://stackoverflow.com/a/31192557/939860 and https://stackoverflow.com/a/11892796/939860 (Also, please ask questions as *question*, not as comment. You can always link to another post for context.) – Erwin Brandstetter Dec 10 '19 at 15:45
  • @ErwinBrandstetter, Thank you so much. I also wrote a question about this on the database administrator exchange, https://dba.stackexchange.com/questions/255308/how-to-check-if-a-value-is-in-an-array-in-a-table If you could post your answer there I'll be most happy to accept it. Again, Thank you! Both for the solution and your tips on how to post. – Kajsa Dec 11 '19 at 08:46
  • 3
    If you have trouble deciphering this into a normal sql query, here's an example: `SELECT id FROM conversation WHERE 45043182 = ANY(participant_user_ids) ;` – oligofren Apr 27 '21 at 13:46
142

Watch out for the trap I got into: When checking if certain value is not present in an array, you shouldn't do:

SELECT value_variable != ANY('{1,2,3}'::int[])

but use

SELECT value_variable != ALL('{1,2,3}'::int[])

instead.

Brad Koch
  • 19,267
  • 19
  • 110
  • 137
murison
  • 3,640
  • 2
  • 23
  • 36
45

but if you have other ways to do it please share.

You can compare two arrays. If any of the values in the left array overlap the values in the right array, then it returns true. It's kind of hackish, but it works.

SELECT '{1}'   && '{1,2,3}'::int[];  -- true
SELECT '{1,4}' && '{1,2,3}'::int[];  -- true
SELECT '{4}'   && '{1,2,3}'::int[];  -- false
  • In the first and second query, value 1 is in the right array
  • Notice that the second query is true, even though the value 4 is not contained in the right array
  • For the third query, no values in the left array (i.e., 4) are in the right array, so it returns false
vol7ron
  • 40,809
  • 21
  • 119
  • 172
  • how can I search for a column from another table to have a value in the array? e.g. select * from beers where style_id in (select preferences from users where id=1) limit 1; style_id is an integer data type; preferences is integer[] I get this error ERROR: operator does not exist: integer = integer[] LINE 1: select * from beers where style_id in (select preferences f... ^ HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts. – H P Apr 29 '16 at 04:19
  • @HP There are different ways to solve that question, you should ask a new question – vol7ron Apr 29 '16 at 14:13
  • are you sure there is no existing question ? @vol7ron – H P Apr 29 '16 at 14:22
  • @HP Not at all, but comments are for comments concerning a question or answer; typically to add more information or solicit more information that wasn't addressed. You're asking a question that is not related to this answer. I think you will have more luck by asking your question as a new post, not in the comment ;) – vol7ron Apr 29 '16 at 14:25
  • @HP if you haven't posted your question you can see here: http://sqlfiddle.com/#!15/144cd/3 for an example of what you need to do -- your issue is different because you need to unnest your array. – vol7ron Apr 29 '16 at 18:45
12

unnest can be used as well. It expands array to a set of rows and then simply checking a value exists or not is as simple as using IN or NOT IN.

e.g.

  1. id => uuid

  2. exception_list_ids => uuid[]

select * from table where id NOT IN (select unnest(exception_list_ids) from table2)

pg2286
  • 1,011
  • 12
  • 21
  • Yes. Note that in my query plans SELECT UNNEST is not as good as = ANY. I'd recommend checking the query plans to see if you get what you want / expect. – Rob Bygrave Oct 30 '17 at 23:32
11

Hi that one works fine for me, maybe useful for someone

select * from your_table where array_column ::text ilike ANY (ARRAY['%text_to_search%'::text]);
Dave Kraczo
  • 748
  • 9
  • 9
7

"Any" works well. Just make sure that the any keyword is on the right side of the equal to sign i.e. is present after the equal to sign.

Below statement will throw error: ERROR: syntax error at or near "any"

select 1 where any('{hello}'::text[]) = 'hello';

Whereas below example works fine

select 1 where 'hello' = any('{hello}'::text[]);
Debasish Mitra
  • 1,394
  • 1
  • 14
  • 17
  • this should be a much more highly upvoted answer. It also clarifies that the type is text[] and not string[] which a great many ppl might assume IF they have experience with other programming languages – Sidharth Ghoshal Oct 18 '22 at 01:21
3

When looking for the existence of a element in an array, proper casting is required to pass the SQL parser of postgres. Here is one example query using array contains operator in the join clause:

For simplicity I only list the relevant part:

table1 other_name text[]; -- is an array of text

The join part of SQL shown

from table1 t1 join table2 t2 on t1.other_name::text[] @> ARRAY[t2.panel::text]

The following also works

on t2.panel = ANY(t1.other_name)

I am just guessing that the extra casting is required because the parse does not have to fetch the table definition to figure the exact type of the column. Others please comment on this.

Kemin Zhou
  • 6,264
  • 2
  • 48
  • 56
0

An additonal note to the accepted answer by @ErwinBrandstetter: If you want to take advantage of an index on the array column when comparing to a singe value, you can wrap your value in an array like this:

select '{1,2,3}'::int[] @> ARRAY[value_variable]

You may need to cast the array type when used parameters though:

select '{1,2,3}'::int[] @> ARRAY[@value_variable]::int[]

Morten Christiansen
  • 19,002
  • 22
  • 69
  • 94