从SQL获得“最高级别”的优雅方式

时间:2018-04-16 20:21:16

标签: sql hierarchy hierarchical-data

我有一个表维护实体和一些任意定义的'级别'之间的关系。 我把它抽象出来就像是亲子关系。 注意 - 我对表的架构没有任何权力。

表:

-- schema
CREATE TABLE Roles (
    Name VARCHAR(25) NOT NULL,
    Role VARCHAR(25) NOT NULL
);

-- data
INSERT INTO Roles
    (Name, Role)
VALUES
('John Smith','GrandParent'),
('John Smith','Parent'),
('John Smith','Child'),
('John Smith','Grandchild'),
('Aaron Chow','Parent'),
('Aaron Chow','Child'),
('Aaron Chow','Grandchild'),
('Bob Johnson','GrandParent'),
('Bob Johnson','Parent'),
('Bob Johnson','Child'),
('Glenda Michaels','Parent'),
('Glenda Michaels','Child'),
('Glenda Michaels','Grandchild'),
('Glenda Michaels','Greatgrandchild'),
('Charlie Doe','Child');

现在,我希望能够查询特定名称,并根据我识别的某个映射获取与该名称关联的“最高级别”。在这种情况下: 祖父母是'最高' 那么父母 然后孩子 然后是孙子 而Greatgrandchild则是“最低”级别

我有一个可以执行此操作的SQL:

SELECT
    CASE
        (
            MIN(
                CASE
                    Role
                    WHEN 'GrandParent' THEN 1
                    WHEN 'Parent' THEN 2
                    WHEN 'Child' THEN 3
                    WHEN 'Grandchild' THEN 4
                    WHEN 'Greatgrandchild' THEN 5
                    ELSE NULL
                END
            )
        )
        WHEN 1 THEN 'GrandParent'
        WHEN 2 THEN 'Parent'
        WHEN 3 THEN 'Child'
        WHEN 4 THEN 'Grandchild'
        WHEN 5 THEN 'Greatgrandchild'
        ELSE NULL
    END
FROM
    Roles
WHERE
    name = 'Aaron Chow'

注意 - 预期结果为“父母”

我在SQL中构建哈希映射,然后获取最小值,然后将该值映射回“级别”。

现在这个SQL有点乱,并且容易出现用户错误(例如,如果值在两个case语句中以差异方式映射)。

有没有更优雅或更健壮的方法来实现这一目标?

编辑:SQLFiddle:http://sqlfiddle.com/#!9/f39b2/5/0

2 个答案:

答案 0 :(得分:0)

扩展我的评论。

我认为保存 "editor.tabSize": 4 属性的新表格会有所帮助。您可以在此处按住roles及其role_name。然后,您可以使用level加入这两个表。

sqlfiddle here

role_id

这里有什么好处,如果你想在未来的某个时候改变你的角色等级(比如说你决定父亲应该超过祖父母,那么你可以这样做:

-- schema
CREATE TABLE User_Roles (
    Name VARCHAR(25) NOT NULL,
    Role_id VARCHAR(25) NOT NULL
);

-- data
INSERT INTO User_Roles
    (Name, Role_id)
VALUES
('John Smith',1),
('John Smith',2),
('John Smith',3),
('John Smith',4),
('Aaron Chow',2),
('Aaron Chow',3),
('Aaron Chow',4),
('Bob Johnson',1),
('Bob Johnson',2),
('Bob Johnson',3),
('Glenda Michaels',2),
('Glenda Michaels',3),
('Glenda Michaels',4),
('Glenda Michaels',5),
('Charlie Doe',3)
;

CREATE TABLE roles (role_id INT, role_name VARCHAR(25), role_level int);

INSERT INTO roles 
VALUES (1, 'GrandParent', 1),
(2, 'Parent', 2),
(3, 'Child', 3),
(4, 'Grandchild', 4),
(5, 'GreatGrandChild', 5);


SELECT role_name 
FROM roles
WHERE role_id =
  (
    SELECT min(role_level) 
    FROM user_roles 
      INNER JOIN roles ON user_roles.role_id = roles.role_id
    WHERE name = 'Aaron Chow'
   );

并且所有结果都会排除在线,而不必使用SQL。

答案 1 :(得分:0)

尝试按级别顺序按角色排序,然后使用LIMIT 1返回顶级。

SELECT role
FROM
roles
WHERE name = 'Aaron Chow'
ORDER BY
               CASE
                    Role
                    WHEN 'GrandParent' THEN 1
                    WHEN 'Parent' THEN 2
                    WHEN 'Child' THEN 3
                    WHEN 'Grandchild' THEN 4
                    WHEN 'Greatgrandchild' THEN 5
                    ELSE 6
                END
LIMIT 1;

可以使用WITH(cte)构建临时结果集,以将每个角色与相应级别相关联。以下是在mySQL 8.0中测试的:

WITH Role_Level as
( SELECT 'GrandParent' as Role,
          1 as Level 
  UNION
  SELECT 'Parent', 2 
  UNION
  SELECT 'Child', 3
  UNION
  SELECT 'Grandchild', 4
  UNION
  SELECT 'Greatgrandchild', 5 )
SELECT r.Role
FROM
Roles r
JOIN
Role_Level l
ON r.Role = l.Role
WHERE r.Name = 'Aaron Chow'
ORDER BY l.Level
LIMIT 1;