I have a database design with multiple many-to-many relationships, and am trying to write a SELECT statement that retrieves a result set with ONE ROW PER USER. To do this, I'd like to aggregate the permissions into a single column.
In the application's database design, a user can have multiple permissions, a role can have multiple permissions, and a user can also have multiple roles, thereby compounding the user's permissions.
Here are the relevant tables:
// Main tables user: - id - username - email role: - id - name - display_name permission: - id - name - display_name // Many-to-many connector tables user_role: - user_id - role_id user_permission: - user_id - permission_id role_permission: - role_id - permission_id (NOT GOOD) I've created a query that successfully aggregates the user permissions, as it's just the single aggregate. However, I can't seem to figure out how to get the 2-dimensional aggregate when adding on the roles' permissions.
(BETTER) From the following query, I'd like to at least have a 6th column called "roles_permissions_array" that in pseudo-code is represented by: "For each role found in the 'user_role_array' column, select the array of all permissions."
(BEST) To take it further, it'd be nice to also have a version of the query that consolidates ALL permissions (both direct-user permissions and role-permissions) into a single column (de-duplicated), so we can see a single source of all permissions per user. In this scenario, the output columns would be: "user_id", "user_name", "user_email", "user_role_array", and "user_permissions_array". The last of these would be a consolidated array of all user permissions AND all role permissions
Here was the beginning of my attempt:
SELECT DISTINCT -- "user" columns u.id as "user_id", u.user_name as "user_user_name", u.email as "user_email", p_agg.user_permissions_array, r_agg.user_role_array FROM users."user" u -- REGULAR JOINS LEFT JOIN users.user_role ur on ur.user_id = u.id LEFT JOIN users.role_permission rp on ur.role_id = rp.role_id -- START AGGREGATES LEFT JOIN ( SELECT array_agg(r.name) as user_role_array, ur.user_id FROM users.user_role ur JOIN users.role r on r.id = ur.role_id GROUP BY ur.user_id ) r_agg using(user_id) LEFT JOIN ( SELECT array_agg(p.name) as user_permissions_array, up.user_id FROM users.user_permission up JOIN users.permission p on up.permission_id = p.id GROUP BY up.user_id ) p_agg using(user_id) -- rp_agg_parent -- NOT WORKING FROM HERE DOWN LEFT JOIN ( SELECT array_agg(role_permission_array) as two_dimensional_role_permissions_array FROM ( SELECT p.role_id, r.name as "role_name", array_agg(p.permission_name) as "role_permission_array" FROM ( SELECT r.id as "role_id", r.name as "role_name", p.name as "permission_name" FROM users.role_permission rp JOIN users.role r on r.id = rp.role_id JOIN users.permission p on p.id = rp.permission_id GROUP BY p.name, r.name, r.id ORDER BY p.name ) as p JOIN users.user_role ur on ur.role_id = p.role_id JOIN users.role r on r.id = ur.role_id GROUP BY r.name, p.role_id order by role_name ) AS rp_agg_sub JOIN users.role_permission rp on rp.role_id = rp_agg_sub.role_id GROUP BY rp_agg_sub.role_name ) rp_agg_parent using(p_role_id) order by u.user_name; But I just get an error: "...column "p_role_id" specified in USING clause does not exist in left table". I suspect even if I got past this error, I might still not get back the desired result set. Any ideas?
a role can have multiple permissions. Do you have tablerole_permission(role_id,permission_id)? This table used in query.p_role_idon the left hand side of your last join, but the join condition isUSING (p_role_id). To be honest, I think there is nowhere ap_role_id.