我有一个具有多个多对多关系的数据库设计,并且正在尝试编写一个
SELECT
语句来检索带有 ONE ROW PER USER 的结果集。为此,我想将权限聚合到单个列中。
在应用的数据库设计中,一个用户可以有多个权限,一个角色可以有多个权限,一个用户也可以有多个角色,从而使用户的权限复合化。
以下是相关表格:
// 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
(不好)我创建了一个成功聚合用户权限的查询,因为它只是单个聚合。但是,我似乎无法弄清楚在添加角色权限时如何获取二维聚合。
(更好)从下面的查询中,我希望至少有一个名为“roles_permissions_array”的第六列,它在伪代码中表示为:“对于在‘user_role_array’列中找到的每个角色,选择所有角色的数组权限。”
(最好)更进一步,最好还有一个查询版本,将所有权限(直接用户权限和角色权限)合并到单个列(去重复)中,这样我们就可以查看每个用户所有权限的单一来源。在这种情况下,输出列将为:
"user_id"
、"user_name"
、"user_email"
、"user_role_array"
和 "user_permissions_array"
。最后一个将是所有用户权限和所有角色权限的合并数组
这是我尝试的开始:
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;
但我只是收到一个错误:
"...column "p_role_id" specified in USING clause does not exist in left table"
。我怀疑即使我克服了这个错误,我可能仍然无法返回所需的结果集。有什么想法吗?
这个问题对我来说并不是100%清楚,但我的解释如下:
users
模式中。试试这个:
SELECT
u.id AS user_id,
u.username AS user_name,
u.email AS user_email,
ARRAY_AGG(DISTINCT r.name ORDER BY r.name) AS user_role_array,
ARRAY_AGG(DISTINCT p.name ORDER BY p.name) AS user_permission_array
FROM users.user u
LEFT JOIN users.user_role ur ON ur.user_id = u.id
LEFT JOIN users.role r ON r.id = ur.role_id
LEFT JOIN users.role_permission rp ON rp.role_id = r.id
LEFT JOIN users.user_permission up ON up.user_id = u.id
LEFT JOIN users.permission p ON p.id IN (rp.permission_id, up.permission_id)
GROUP BY u.id
ORDER BY u.username;