SQL查询检索两个与外部联接相关的多对多对多表的表

时间:2013-01-09 16:20:53

标签: sql select join left-join

我的SQL感觉今天早上没有用,我需要一些帮助。

我有3个主表,它们之间有两个用于多对多关系的中间表:userusers_groupsgroupgroups_permissions和{ {1}}。每个permission都有permission

我想编写一个查询,给定权限名称和用户ID,如果权限存在,则返回相应的权限ID ,如果用户具有其他权限,则返回值空值。也就是说,我想知道权限是否存在以及用户是否在一个查询中具有权限。输出应该如下所示:

name

现在,我能想到的查询看起来像这样:

                         |has_permission | does_not_have_permission
-------------------------------------------------------------------
permission_exists        |     (1,1)     |        (1,NULL)
permission_does_not_exist|      N/A      |        [0 rows]

问题是在某些情况下会返回多行。我想,我可以将SELECT p.id, ug.user_id FROM permission AS p LEFT JOIN groups_permissions AS gp ON p.id = gp.permission_id LEFT JOIN group AS g ON gp.group_id = g.id LEFT JOIN users_groups AS ug ON ug.group_id = g.id AND ug.user_id = ? WHERE p.name = ?; ORDER BY u.id DESC扔到它上面,但这样可以很好地优化吗?我需要什么索引?有没有其他方法可以编写此查询以快速轻松地获取我正在寻找的信息?

编辑:对于那些好奇的人,我正在使用PostgreSQL,尽管如果可能,我希望查询与数据库无关。

3 个答案:

答案 0 :(得分:2)

添加distinct是否适用于您的查询?

SELECT distinct p.id, ug.user_id
FROM permission AS p LEFT JOIN
     groups_permissions AS gp ON p.id = gp.permission_id LEFT JOIN
     group AS g ON gp.group_id = g.id LEFT JOIN
     users_groups AS ug ON ug.group_id = g.id AND ug.user_id = ?
WHERE permission.name = ?;

我认为问题在于某些用户从不同的群组成员身份获得相同的权限。

评论中的问题可以通过聚合修复:

SELECT p.id, max(ug.id),
       (case when max(ug.id) is null then 'DENIED' else 'ALLOWED' end)
FROM permission AS p LEFT JOIN
     groups_permissions AS gp ON p.id = gp.permission_id LEFT JOIN
     group AS g ON gp.group_id = g.id LEFT JOIN
     users_groups AS ug ON ug.group_id = g.id AND ug.user_id = ?
WHERE permission.name = ?
group by ug.id

答案 1 :(得分:1)

SELECT p.id, u.user_id
FROM permission AS p 
  LEFT JOIN
    user AS u 
      ON  u.user_id = ? 
      AND EXISTS
          ( SELECT *
            FROM users_groups AS ug 
              JOIN groups_permissions AS gp 
                ON gp.group_id = ug.group_id
            WHERE p.id = gp.permission_id 
              AND u.user_id = ug.user_id
          )
WHERE p.name = ? ;

答案 2 :(得分:0)

抱歉 - 刚看到原始问题底部的PostreSQL和db不可知句子。此答案可能仅适用于MS SQL Server。

如果用户属于具有相同权限的多个组,则可能会获得多行。在这种情况下,您需要聚合它们。这样的事情会起作用吗?

WITH PermissionsForUser AS
(
SELECT
  p.id,
  u.id
FROM permissions p
LEFT JOIN groups_permissions gp
ON p.id = gp.permission_id
LEFT JOIN users_groups ug
ON ug.group_id = gp.group_id
LEFT JOIN users u
ON ug.user_id = u.id
WHERE p.name = @PermissionId
AND u.id = @UserId
)
SELECT
  MAX(p.id) AS permission_id
  MAX(u.id) AS user_id
FROM PermissionsForUser;