Reputation: 107728
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?
Upvotes: 0
Views: 2591
Reputation: 42109
If you really wanted to you could perform a join.
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
Upvotes: 1
Reputation: 1153
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"
Upvotes: 1