【发布时间】:2021-09-26 17:15:28
【问题描述】:
我在 mariaDB/mysql 上遇到了递归 cte 的问题。设置并不太复杂:有一个users 表、一个roles 表和一个为用户分配角色的user_roles 表。但是,角色可以嵌套,roles 表包含一个parent_id 字段来完成此操作。 parent_id 字段在它是根角色时将为 NULL,如果不是,则包含另一个角色的 ID。
这可以通过以下创建和插入进行设置:
CREATE TABLE users (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(255),
PRIMARY KEY (id)
);
CREATE TABLE roles (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(255),
parent_id INT,
PRIMARY KEY (id)
);
CREATE TABLE user_roles (
id INT NOT NULL AUTO_INCREMENT,
user_id INT,
role_id INT,
PRIMARY KEY (id)
);
INSERT INTO users(name) VALUES ('Alice'),('Bob'),('Charlie');
INSERT INTO roles(name, parent_id) VALUES
('superuser',null),
('admin',1),
('ceo',1),
('employee', null);
INSERT INTO user_roles(user_id, role_id) VALUES
(1,2),
(2,3),
(3,4);
我的目标是创建一个查询,以选择其中一个用户角色(或其中一个管理员)与给定角色名称匹配的所有用户。我想出了这个:
SELECT * FROM users
WHERE EXISTS(
SELECT 1 FROM roles
INNER JOIN user_roles ON roles.id = user_roles.role_id
WHERE users.id = user_roles.user_id
AND EXISTS(
WITH RECURSIVE cte AS (
SELECT * FROM roles as roles_recursive
WHERE roles_recursive.id = roles.id
UNION ALL
SELECT roles_recursive.* FROM roles as roles_recursive
INNER JOIN cte ON cte.parent_id = roles_recursive.id
)
SELECT 1 from cte WHERE name='superuser'
)
);
我设置了DB fiddle 来重现我的问题。请在那里查看完整的复制案例。 这会在不同的引擎上产生不同的结果:
- MySQL 8.0.18:我目前的开发机器只运行macOS 10.13,所以我仅限于这个MySQL版本。在这个版本中,我只会得到“Alice”,而“Bob”也应该出现在结果中。
- DB fiddle,MariaDB 10.3.30:产生错误:“ER_BAD_FIELD_ERROR:'where 子句'中的未知列 'roles.id'”。看起来角色范围在 CTE 中不可用。
- dbfiddle.uk 似乎正在运行另一个版本的 MySQL,并且确实返回了正确的结果集。
有什么方法可以做到这一点,至少在 MariaDB 中有效?我不是在寻找具有简单连接的解决方案,角色应该支持可变深度。
【问题讨论】:
-
您好,请将您的设置也添加到您的问题中,小提琴网站上的链接是临时的,这会使您的问题难以被未来的读者理解。
-
供将来参考:MariaDB 并不真正支持这一点:jira.mariadb.org/browse/MDEV-19337
标签: mysql sql mariadb common-table-expression