14

I have two arrays [1,2,3,4,7,6] and [2,3,7] in PostgreSQL which may have common elements. What I am trying to do is to exclude from the first array all the elements that are present in the second. So far I have achieved the following:

SELECT array
  (SELECT unnest(array[1, 2, 3, 4, 7, 6])
   EXCEPT SELECT unnest(array[2, 3, 7]));

However, the ordering is not correct as the result is {4,6,1} instead of the desired {1,4,6}. How can I fix this ?


I finally created a custom function with the following definition (taken from here) which resolved my issue:

create or replace function array_diff(array1 anyarray, array2 anyarray)
returns anyarray language sql immutable as $$
    select coalesce(array_agg(elem), '{}')
    from unnest(array1) elem
    where elem <> all(array2)
$$;
1
  • 1
    You could install the intarray extension which offers an operator for that. Commented Mar 22, 2019 at 17:52

3 Answers 3

9

I would use ORDINALITY option of UNNEST and put an ORDER BY in the array_agg function while converting it back to array. NOT EXISTS is preferred over except to make it simpler.

SELECT array_agg(e order by id) 
   FROM unnest( array[1, 2, 3, 4, 7, 6] ) with ordinality as s1(e,id)
    WHERE not exists 
   (
     SELECT 1 FROM unnest(array[2, 3, 7]) as s2(e)
      where s2.e = s1.e
    )

DEMO

Sign up to request clarification or add additional context in comments.

2 Comments

I will accept the answer since it resolves the initial question. However, I ended up creating a custom function which computes the "difference" of two arrays, the definition of which I have added in the initial question.
Instead of using NOT EXISTS you could simplify to WHERE NOT s1.e = ANY(ARRAY[2, 3, 7])
0

I took Kaushik Nayak's answer and made it into a custom operator for easier use

create or replace function array_difference(anyarray, anyarray)
returns anyarray language sql immutable as $$
    SELECT coalesce(array_agg(e order by id), '{}') 
    FROM unnest( $1 ) with ordinality as s1(e,id)
    WHERE not exists 
    (
      SELECT 1 FROM unnest($2) as s2(e)
      where s2.e = s1.e
    )
$$;

create operator - (
    leftarg = anyarray, 
    rightarg = anyarray, 
    procedure = array_difference, 
    commutator = -);

Usage:

select '{1,2,3,4}'::int[] - '{4}'::int[]
-- '{1,2,3}'::int[]
select '{1,2,3,4}'::text[] - '{4}'::text[]
-- '{1,2,3}'::text[]

select '{1,2,3,4,4}'::int[] - '{4}'::int[]
-- '{1,2,3}'::int[]
select '{1,2,3,4,4}'::text[] - '{4}'::text[]
-- '{1,2,3}'::text[]

select '{4}'::text[] - '{1,2,3,4}'::text[]
-- '{}'::text[]
select '{4}'::int[] - '{1,2,3,4}'::int[]
-- '{}'::int[]

1 Comment

commutator = - is not strictly true, as the order in the result array depends on the first operand.
-2

Postgres is unfortunately lacking this functionality. In my case, what I really needed to do was to detect cases where the array difference was not empty. In that specific case you can do that with the @> operator which means "Does the first array contain the second?"

ARRAY[1,4,3] @> ARRAY[3,1,3] → t

See doc

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.