'Using array_agg with multiple DISTINCT Columns
In this query, I'm listing all users in organization 123 but I also want a column showing which other teams they are on across all organizations.
My query right now will give me the team names but I'd also like to get the team id as well. The DISTINCT is necessary because they user may have different roles on the same team.
Bonus points if I can sort the teams by when the user was given a role, which currently gives an error as I have it now.
SELECT
users.*,
(
SELECT
to_json(array_agg(DISTINCT teams.name ORDER BY teams.name))
FROM roles r
INNER JOIN user_roles ur ON ur.role_id=r.id AND ur.user_id=users.id
INNER JOIN teams ON r.team_id=teams.id
-- ORDER BY r.created_at
) teams
FROM users
INNER JOIN user_roles ON users_roles.user_id=users.id
INNER JOIN roles ON roles.id = user_roles.role_id
WHERE roles.type = 'admin' AND roles.organization_id = 123
GROUP BY users.id
This returns:
name | teams
John Smith | ['Team 1', 'Team 2']
Jane Doe | ['Team 2', 'Team 3']
What I'd like to return is the team name with its primary key id:
name | teams
John Smith | {1: 'Team 1', 2: 'Team 2'}
Jane Doe | {2: 'Team 2', 3: 'Team 3'}
EDIT
Or better yet:
name | teams
John Smith | [{id: 1, name: 'Team 1'}, {id: 2, 'Team 2'}]
Jane Doe | [{id: 2, name: 'Team 2'}, {id: 3, 'Team 3'}]
Solution 1:[1]
Considering that your query is working fine, replace the following section of your query mentioned in question:
SELECT
to_json(array_agg(DISTINCT teams.name ORDER BY teams.name))
FROM roles r
INNER JOIN user_roles ur ON ur.role_id=r.id AND ur.user_id=users.id
INNER JOIN teams ON r.team_id=teams.id
-- ORDER BY r.created_at
with
(SELECT
json_object(array_agg(id::text order by created_at desc),
array_agg(name order by created_at desc)) from
( SELECT
DISTINCT on (teams.id) teams.id, teams.name , r.created_at
FROM roles r
INNER JOIN user_roles ur ON ur.role_id=r.id AND ur.user_id=users.id
INNER JOIN teams ON r.team_id=teams.id
ORDER BY r.created_at
)tab)
Solution 2:[2]
Here is how I ended up solving it, building off of @akhilesh answer.
SELECT
json_object(
array_agg(id :: text ORDER BY created_at DESC),
array_agg(name ORDER BY created_at DESC)
)
FROM
(
SELECT
*
FROM
(
SELECT
teams.id,
teams.name,
MAX(ur.created_at) created_at
FROM
roles r
INNER JOIN user_roles ur ON ur.role_id = r.id AND ur.user_id = users.id
INNER JOIN teams ON r.team_id=teams.id
GROUP BY
teams.id
) T
ORDER BY
T.created_at DESC
) teams
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Akhilesh Mishra |
| Solution 2 | Dex |
