使用 1:1 链接表和 CTE(例如,对用户和组的权限)查找与其他关联的 SQL 行

Find SQL rows associated to other using 1:1 linking tables and CTE (eg. permissions on users and groups)

问题

我需要确定用户 U 是否可以根据存储在 MySQL tables(在 MariaDB 10.2 上)的权限使用以下内容访问实体 E规则:

tables 已经到位,我理解我的问题的方式归结为: “在用户行 U 和实体行 E 之间是否存在 link(通过任何可能的路径)?”

因为 table 没有图表(请参阅下面的详细信息)并且因为组继承我想我可以在这里使用 Common Table 表达式(我不熟悉),递归(对我来说更糟)。

我的尝试

所以我构建了以下 SQL 查询以支持直接实体用户津贴(第一个 CTE:cte_entities_allowed_to_users

SET @TEST_EID = 302;
SET @TEST_UID = 103;

WITH
cte_entities_allowed_to_users AS (
    SELECT
        DISTINCT
        entities.eid,
        entity_user_access.uid
    FROM
        entity_user_access
        JOIN entities
            ON entity_user_access.eid = entities.eid
)
SELECT
    COUNT(*)
FROM
    cte_entities_allowed_to_users
WHERE
    cte_entities_allowed_to_users.eid = @TEST_EID
    AND cte_entities_allowed_to_users.uid = @TEST_UID

并且还构建了以下其他 CTE:

cte_groups_of_users AS (
    SELECT
        DISTINCT
        users.uid,
        groups.gid
    FROM
        groups
        JOIN group_members
            ON group_members.gid = groups.gid
        JOIN users
            ON users.uid = group_members.uid
)
,
cte_entities_allowed_to_groups AS (
    SELECT
        DISTINCT
        entities.eid,
        entity_group_access.gid
    FROM
        entity_group_access
        JOIN entities
            ON entity_group_access.eid = entities.eid
)

我奋斗的地方

但我不确定:

我所期望的(“真相table”)

对于以下 @TEST_EID@TEST_UID 对,这里是 SELECT(COUNT(*) 字段)的预期返回值:

@TEST_EID @TEST_UID Return Why
301 101 ≥1 entity_user_access has a (301,101) row
301 102 ≥1 entity_user_access has a (301,102) row
301 103 =0
302 101 =0
302 102 ≥1 entity_group_access has a (302,201) row and group_members has a (201,102) row
302 103 ≥1 entity_user_access has a (302,103) row
303 101 =0
303 102 ≥1 (303,201) row in entity_group_access and group_members has a (201,102) row
303 103 ≥1 entity_user_access has a (303,103) row
304 101 ≥1 (304,205) row in entity_group_access and group_of_groups has a(204,205) row and group_members has a (204,101) row
304 102 ≥1 (304,201) row in entity_group_access and group_members has a (201,102) row
304 103 ≥1 (304,205) row in entity_group_access and group_of_groups has a(202,205) row and group_members has a (202,103) row

附件 1/1:Table详细信息

关系:


      [users]───────────────[group_members]─────[groups]──┐
         │                       │                 │      │
         │                       │                 └──[group_of_groups]
[entity_user_access]   [entity_group_access]
         │                       │
         └────────┐    ┌─────────┘
                  │    │
                [entities]

一些示例数据集:

users table:

uid uname
101 Alice
102 Bob
103 Charlie

groups table:

gid gname
201 Administrators
202 Users
203 Operators
204 Guests
205 X-Mas event

entities table:

eid ename
301 Foo
302 Bar
303 Qux
304 Snow

entity_user_access table:

eid uid
301 101
301 102
302 103
303 103

entity_group_access table:

eid gid
301 201
302 201
303 201
304 201
302 203
304 205

group_members table:

gid uid Comment (not part of data)
201 102 Bob is an admin
203 102 Bob is also an operator
202 103 Charlie is an user
204 101 Alice is a guest

group_of_groups table:

gid parent_gid Comment (not part of data)
201 203 Admins (201) are Operators (203)
202 205 Users (202) are in the X-Mas event group (205)
204 205 Guests (204) are in the X-Mas event group (205)

完整 SQL:

-- Structure
CREATE TABLE `users` (
    `uid` INT(11) NOT NULL,
    `uname` TINYTEXT NOT NULL,
    PRIMARY KEY (`uid`)
);
CREATE TABLE `groups` (
    `gid` INT(11) NOT NULL,
    `gname` TINYTEXT NOT NULL,
    PRIMARY KEY (`gid`)
);
CREATE TABLE `entities` (
    `eid` INT(11) NOT NULL,
    `ename` TINYTEXT NOT NULL,
    PRIMARY KEY (`eid`)
);
CREATE TABLE `entity_user_access` (
    `eid` INT(11) NOT NULL,
    `uid` INT(11) NOT NULL,
    PRIMARY KEY (`eid`, `uid`),
    CONSTRAINT `FK_eua_entity` FOREIGN KEY (`eid`) REFERENCES `entities` (`eid`),
    CONSTRAINT `FK_eua_user` FOREIGN KEY (`uid`) REFERENCES `users` (`uid`)
);
CREATE TABLE `entity_group_access` (
    `eid` INT(11) NOT NULL,
    `gid` INT(11) NOT NULL,
    PRIMARY KEY (`eid`, `gid`),
    CONSTRAINT `FK_ega_entity` FOREIGN KEY (`eid`) REFERENCES `entities` (`eid`),
    CONSTRAINT `FK_ega_group` FOREIGN KEY (`gid`) REFERENCES `groups` (`gid`)
);
CREATE TABLE `group_members` (
    `gid` INT(11) NOT NULL,
    `uid` INT(11) NOT NULL,
    PRIMARY KEY (`gid`, `uid`),
    CONSTRAINT `FK_gm_group` FOREIGN KEY (`gid`) REFERENCES `groups` (`gid`),
    CONSTRAINT `FK_gm_user` FOREIGN KEY (`uid`) REFERENCES `users` (`uid`)
);
CREATE TABLE `group_of_groups` (
    `gid` INT(11) NOT NULL,
    `parent_gid` INT(11) NOT NULL,
    PRIMARY KEY (`gid`, `parent_gid`),
    CONSTRAINT `FK_gog_group` FOREIGN KEY (`gid`) REFERENCES `groups` (`gid`),
    CONSTRAINT `FK_gog_parent_group` FOREIGN KEY (`parent_gid`) REFERENCES `groups` (`gid`)
);

-- Data
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
INSERT INTO `users` VALUES
    (101, 'Alice'),
    (102, 'Bob'),
    (103, 'Charlie');
INSERT INTO `groups` VALUES
    (201, 'Administrators'),
    (202, 'Users'),
    (203, 'Operators'),
    (204, 'Guests'),
    (205, 'X-Mas event');
INSERT INTO `entities` VALUES
    (301, 'Foo'),
    (302, 'Bar'),
    (303, 'Qux'),
    (304, 'Snow');
INSERT INTO `entity_user_access` VALUES
    (301, 101),
    (301, 102),
    (302, 103),
    (303, 103);
INSERT INTO `entity_group_access` VALUES
    (301, 201),
    (302, 201),
    (302, 203),
    (303, 201),
    (304, 201),
    (304, 205);
INSERT INTO `group_members` VALUES
    (201, 102),
    (202, 103),
    (203, 102),
    (204, 101);
INSERT INTO `group_of_groups` VALUES
    (201, 203),
    (202, 205),
    (204, 205);
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;

CTE 只是子查询的替代品。

但是你的处理方式有点离谱

您的群组 table 需要与第一个群组具有相同的结构,因此您必须加入其他 table 群组才能获得 eid 和 uid

下一步是 UNION 结果,以便它们垂直连接

在那个工会上你 运行 你的计数查询

查看示例

SET @TEST_EID = 302;
SET @TEST_UID = 103;
SELECT COUNT(*)
FROM
(    SELECT
        DISTINCT
        entities.eid,
        entity_user_access.uid
    FROM
        entity_user_access
        JOIN entities
            ON entity_user_access.eid = entities.eid
UNION
    SELECT
        DISTINCT
        entities.eid,
        users.uid
    FROM
        `groups`
        JOIN group_members
            ON group_members.gid = groups.gid
        JOIN users
            ON users.uid = group_members.uid
         JOIN 
            entity_group_access
            ON groups.gid = entity_group_access.gid
         JOIN entities
            ON entity_group_access.eid = entities.eid) t1
WHERE eid = @TEST_EID and uid = @TEST_UID
| COUNT(*) |
| -------: |
|        1 |

db<>fiddle here