0

I have a users table with this structure:

CREATE TABLE users (
  id integer NOT NULL,
  email text,
  account_id integer,
  segment_ids integer[]
);

And a segments table with this structure:

CREATE TABLE segments (
  id integer NOT NULL,
  name text
);

I have built a query like this to find all the users which belong to segments:

SELECT * FROM users 
  WHERE segment_ids @> (
    SELECT ARRAY(
      SELECT id from segments 
        WHERE name IN ('Melbourne', 'Male', 'Team #1')
    )
  )

However, I am not satisfied that using a subquery is the best way to accomplish this. Is there a cleaner way of writing this query, i.e. with a joins, or is a subquery the way to go?

0

2 Answers 2

2

If you really wanted to you could perform a join.

Fiddle

SELECT DISTINCT users.* 
FROM users 
JOIN segments 
  ON ( segments.id = ANY( users.segment_ids ) )
ORDER BY 1;

And if you really need to see what the segment_id that it matched:

SELECT DISTINCT users.*, array_agg(segments.id) "matched segment id"
FROM users 
JOIN segments 
  ON ( segments.id = ANY( users.segment_ids ) )
GROUP BY users.id, email, account_id, segment_ids
ORDER BY 1;

Insert your WHERE segments.name IN(...) wherever you see fit


Credit: @JamesFry for the DDL statements in the fiddle

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

1 Comment

Thank you @vol7tron but I think this doesn't use a WHERE like I specified in my original question. How would I use a WHERE in your query?
1

You could use the unnest function to do this:

For example, with the above tables and this data:

insert into segments values (1, 'one');
insert into segments values (2, 'two');
insert into segments values (3, 'three');

insert into users values (1, '[email protected]', 1, array[1,2]);
insert into users values (2, '[email protected]', 2, array[2,3]);

Running the following query:

select u.id, u.email, u.account_id, u.segment_id, s.name from 
(select u.id, u.email, u.account_id, unnest(u.segment_ids) segment_id from users u) as u 
right join segments s on s.id = u.segment_id

yields:

1   "[email protected]"     1   1   "one"
1   "[email protected]"     1   2   "two"
2   "[email protected]"    2   2   "two"
2   "[email protected]"    2   3   "three" 

1 Comment

Could I then add a WHERE on the end to match segment names, only returning the users that have the combination of segments specified?

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.