MySQL JOIN / UNION DISTINCT 问题

Posted

技术标签:

【中文标题】MySQL JOIN / UNION DISTINCT 问题【英文标题】:MySQL JOIN / UNION DISTINCT issue 【发布时间】:2019-12-09 23:04:28 【问题描述】:

我正在开展一个项目,其中用户需要根据他们连接的部门获得权限。

所以现在我正在尝试执行一个查询,该查询将根据用户部门 ID 向我显示具有所有必要权限的用户(不具备所有必要权限的用户不应显示在结果中)。

我一直在尝试很多不同的事情(不同类型的 JOINS、子查询 w/where in、exists 和 union distinct),但没有运气,这种查询对我来说是下一个层次,挑战了我的逻辑思维。

表格:

users
    id
    first_name
    last_name
    department_id

permissions
    id
    name

departments (relation to groups and categories based on department)
    id 
    name
    category_id
    group_id

categories
    id
    name

groups
    id
    name

departments_permissions (permission requirement)
    department_id
    permission_id

categories_permissions (permission requirement)
    category_id
    permission_id

groups_permissions (permission requirement)
    group_id
    permission_id

users_permissions (users have permissions here)
    user_id
    permission_id

部门

一个部门 CAN(非必需)拥有许多权限 (departments_permissions)。 一个部门可以(非必需)有一个类别。 一个部门可以(非必需)有一个组。

类别

一个类别 CAN(非必需)具有许多权限 (categories_permissions)。

一个组 CAN(非必需)具有许多权限 (groups_permissions)。

重要的旁注

departments_permissions、categories_permissions 和 groups_permissions 可以包含一个或多个相同的 permission_id,因此我们必须选择不同的值。

示例数据

https://pastebin.com/BvhAznpY

我最近的查询尝试:

SELECT 
    `users`.id, 
    `users`.first_name, 
    `users`.last_name,
    `users`.department_id
FROM 
    `users`
INNER JOIN 
    `departments` ON `users`.department_id = `departments`.id 
INNER JOIN 
    `users_permissions` ON `users`.id = `users_permissions`.user_id 
INNER JOIN 
    (
        SELECT permission_id FROM departments_permissions WHERE department_id = departments.id
        UNION DISTINCT
        SELECT permission_id FROM categories_permissions WHERE category_id = departments.group_id
        UNION DISTINCT
        SELECT permission_id FROM groups_permissions WHERE group_id = departments.group_id
    ) AS tblPermissionsRequired ON `users_permissions`.permission_id = `tblPermissionsRequired`.permission_id
GROUP BY 
    `users_permissions`.user_id;

【问题讨论】:

来自不同表的样本数据与您的预期输出会有所帮助。 @mkRabbani 我已经更新了我的问题(参见 pastebin url)。 您没有详细说明查询应该返回什么。用户何时“拥有基于用户部门 ID 的所有必要权限”? “基于”模糊总结的确切条件是什么? PS请将您的问题所需的所有内容作为剪切粘贴和可运行的文本放入您的问题中。 minimal reproducible example PS您给出了您的情况/状态的某些方面,即对表值的一些约束,但是您需要为每个表(基本和查询结果)告诉我们其中一行关于情况/状态的说明它的列值。 @philipxy 我想从 users 表中检索行(查看我最近的查询尝试,特别是选择部分)。一个用户拥有所有必要的权限,当 users_permissions 包含通过 department_id -> 部门 -> 部门_permissions、categories_permissions、groups_permissions 与用户相关的所有权限时。在任何权限表中可能有也可能没有权限要求。如果有,请将所有不同的权限与用户权限匹配。查看 pastebin url 以查找示例数据和预期输出示例。 A minimal reproducible example 包括剪切&粘贴&可运行代码;具有期望和实际输出的示例输入(包括逐字错误消息);标签;明确的规范和解释。这包括您可以提供的最少代码,即您显示的代码可以通过您显示的代码扩展为不可以。 (调试基础。) 【参考方案1】:

建立2个表(子查询)来列出

用户所需的所有权限(即 user_permissions) 用户拥有的所有权限(即与您所做的类似的 3 个联合)

加入它们并按 id 分组将找到所需的数量以及已经给出的数量。

最后,添加一个过滤子句(使用 HAVING)。然后你得到了用户 ID 列表:)

select
  up.user_id,
  max(u.first_name) as first_name,
  max(u.last_name) as last_name,
  count(up.permission_id) as permission_required,
  count(perm.permission_id) as permission_have
from
  users u
join
  users_permissions up on u.id = up.user_id
left join
(
  SELECT users.id as user_id, permission_id
  FROM users
  INNER JOIN departments ON users.department_id = departments.id
  INNER JOIN departments_permissions on departments_permissions.department_id = departments.id

  UNION

  SELECT users.id, permission_id
  FROM users
  INNER JOIN departments ON users.department_id = departments.id
  INNER JOIN categories_permissions on categories_permissions.category_id = departments.category_id

  UNION

  SELECT users.id, permission_id
  FROM users
  INNER JOIN departments ON users.department_id = departments.id
  INNER JOIN groups_permissions  on groups_permissions.group_id = groups_permissions.group_id
) perm
on up.user_id = perm.user_id and up.permission_id = perm.permission_id
group by up.user_id
having count(up.permission_id)  = count(perm.permission_id)

【讨论】:

这看起来不错!谢谢!将对其进行测试并提供反馈。请问你为什么使用 max(u.first_name) ? 我们需要按 user_id 分组,因此我们需要添加聚合函数(count、max 等)来显示其他未分组的列。另一种方法是更改​​所有内容的分组。例如"group by up.user_id, u.first_name, u.last_name",那么就不用max了 这很好用@Patrick。如果我不想通过将最后的比较更改为“有计数(up.permission_id)!=计数(perm.permission_id)”来进行相反的检查,它也可以工作。但是,当我进行相反的检查时,它将忽略 users_permissions 表中没有任何权限的用户。试图离开加入第一个加入以保留所有用户,但它没有帮助。对这个案例有什么建议吗?【参考方案2】:

我认为你需要的是这样的:

SELECT u.first_name,u.last_name,p.name
FROM users u, user_permissions up, permissions p
WHERE u.id=up.user_id AND
up.permission_id=p.id
UNION
SELECT u.first_name,u.last_name,p.name
FROM users u, departments d, departments_permission dp, permissions p
WHERE u.department_id=d.id
AND d.id=dp.department_id
AND dp.permission_id=p.id
UNION
SELECT u.first_name,u.last_name,p.name
FROM users u, departments d, categories_permission cp, permissions p
WHERE u.department_id=d.id
AND d.category_id=cp.category_id
AND cp.permission_id=p.id
UNION
SELECT u.first_name,u.last_name,p.name
FROM users u, departments d, groups_permission gp, permissions p
WHERE u.department_id=d.id
AND d.group_id=gp.group_id
AND gp.permission_id=p.id

这里有一个提示:

使用上面的查询创建一个视图 在视图上执行 SELECT DISTINCT *

【讨论】:

“users_permissions”表不在此查询中,因此无法与实际授予的用户权限进行比较。 哎呀我忘了把它添加到查询的顶部。你想让我编辑我的答案来涵盖这个吗? 我已经修复了查询,希望它现在是正确的。 非常感谢,非常感谢所有帮助。我稍后会试试看。 很高兴能帮上忙。我试图使查询尽可能简单。【参考方案3】:

以下脚本应该可以工作-

SELECT A.id,A.first_name,A.last_name,A.department_id 
FROM
(
    SELECT U.*,
    A.Dept_id AS Dept_ID,
    A.permission_id AS [Dept_Wise_Permissiion_List],
    UP.permission_id
    FROM users U
    INNER JOIN (
            SELECT D.id Dept_id,CP.permission_id  
            FROM departments D INNER JOIN categories_permissions CP ON D.category_id = CP.category_id 

            UNION ALL

            SELECT D.id Dept_id,GP.permission_id  
            FROM departments D INNER JOIN groups_permissions GP ON D.group_id = GP.group_id

            UNION ALL

            SELECT D.id Dept_id, DP.permission_id
            FROM departments D INNER JOIN departments_permissions DP ON D.id = DP.department_id

    ) A ON U.department_id =A. Dept_id
    LEFT JOIN users_permissions UP 
        ON U.id = UP.user_id AND A.permission_id = UP.permission_id
)A
GROUP BY A.id,A.first_name,A.last_name,A.department_id 
HAVING COUNT(ISNULL(A.permission_id,1)) = COUNT(A.permission_id)

【讨论】:

Ola 在示例数据中的 department_id 为 2。因此,如果我们查看部门权限,Ola 还需要权限 4(所以,1、3 和 4)。感谢您的回答,我一会儿测试一下。 Ola 需要多少:4、3、1? Ola 的部门为 2,在部门权限表中只有权限 id = 4 的部门 2 的条目。 Ola 的部门是 2。部门 2 有类别 1 和组 2。Ola 需要从部门权限 4 和组权限 1 和权限 3 来自 category_permissions 的权限。 明白你的意思。我已经更新了脚本,它现在应该可以工作了。

以上是关于MySQL JOIN / UNION DISTINCT 问题的主要内容,如果未能解决你的问题,请参考以下文章

MySQL JOIN 的结果 UNION

mysql中left join和union有啥区别?

强制 MySQL 从 WHERE IN 子句返回重复项而不使用 JOIN/UNION?

Mysql union/join 对多列的帮助

MYSQL FULL OUTER JOIN - 使用 LEFT-UNION-LEFT JOIN 时的所有 NULL 结果

MySQL:在没有 JOIN 或 UNION 的情况下合并两个不同的表