-1

Member-Role 是 N:N 关系。

Member           MemberRole       Role
+----------+     +----------+     +----------+
| Id       |     | MemberId |     | Id       |
| ...      |     | RoleId   |     | Name     |
+----------+     +----------+     +----------+
  • 大约有 15,000 名成员,他们的角色数量不一且可能为零。
  • 大约有 50 个角色。

在数据库之外,我有需要检查数据库的允许-拒绝列表。列表可能看起来像

+a -b +c +d

这表示:

  1. 如果有角色a
    1. 允许
  2. 别的,
    1. 如果有角色b
      1. 否定
    2. 别的,
      1. 如果有角色c
        1. 允许
      2. 别的,
        1. 如果有角色d
          1. 允许
        2. 别的,
          1. 否定

例如,具有角色ad的人将被允许,而具有角色bd的人将被禁止。

通过从末尾开始,对项应用并集 (∪) 对+项应用差 (-),可以轻松地将列表转换为数学方程-

+a -b +c +d⇒ ( ( ( ∅ ∪ d ) ∪ c ) - b ) ∪ a

任何尾随的拒绝 ( -) 都可以忽略,因此我们知道第一个将始终是联合。

+a -b +c +d⇒ ( ( d ∪ c ) - b ) ∪ a

由此,我可以构建以下查询:

       SELECT `MemberId` FROM `MemberRole` WHERE `RoleId` = @d
UNION  SELECT `MemberId` FROM `MemberRole` WHERE `RoleId` = @c
EXCEPT SELECT `MemberId` FROM `MemberRole` WHERE `RoleId` = @b
UNION  SELECT `MemberId` FROM `MemberRole` WHERE `RoleId` = @a

由于 x - y = x ∩ y',我们还可以推导出

+a -b +c +d⇒ ( ( d ∪ c ) ∩ b' ) ∪ a

由此,我可以构建以下查询:

SELECT `Id`
  FROM `Member`
 WHERE (
           (
               EXISTS ( SELECT * FROM `MemberRole` WHERE `MemberId` = `Member`.`Id` AND `RoleId` = @d )
               OR
               EXISTS ( SELECT * FROM `MemberRole` WHERE `MemberId` = `Member`.`Id` AND `RoleId` = @c )
           )
           AND
           NOT EXISTS ( SELECT * FROM `MemberRole` WHERE `MemberId` = `Member`.`Id` AND `RoleId` = @b )
       )
       OR
       EXISTS ( SELECT * FROM `MemberRole` WHERE `MemberId` = `Member`.`Id` AND `RoleId` = @a )

检查成员是否被允许的最佳方法是什么?(答案通常是肯定的。)会使用WITH帮助吗?

请注意,允许-拒绝列表的项目可以是角色 ID(数字)或角色名称(不是数字)。

4

2 回答 2

2

允许规则+a -b +c +d可以用逻辑表达式来描述a + !b(c + d)

WITH MemberRole_acl AS (
  SELECT memberId,
    SUM(roleId = 'a') AS rolesA,
    SUM(roleId = 'b') AS rolesB,
    SUM(roleId = 'c') AS rolesC,
    SUM(roleId = 'd') AS rolesD
  FROM MemberRole
  GROUP BY memberId
)
SELECT m.*
FROM Member m
JOIN MemberRole_acl r ON r.memberId = m.id
WHERE rolesA OR NOT rolesB AND (rolesC OR rolesD)

db<>小提琴

于 2021-11-01T02:20:05.360 回答
1

如果角色名称为@a、@b、@c 和@d,则可以在HAVING子句中加入表、聚合和设置条件:

SELECT mr.memberid
FROM MemberRole mr INNER JOIN Role r
ON r.Id = mr.RoleId
WHERE r.Name IN (@a, @b, @c, @d)
GROUP BY mr.memberid
HAVING MAX(r.Name = @a) OR NOT MAX(r.Name = @b); 

如果您有@a、@b、@c 和@d 等角色的ID,则更简单:

SELECT memberid
FROM MemberRole 
WHERE RoleId IN (@a, @b, @c, @d)
GROUP BY memberid
HAVING MAX(RoleId = @a) OR NOT MAX(RoleId = @b); 
于 2021-11-01T12:19:00.193 回答