
树形结构查询父子层级闭包表-空间换时间
表设计
部门表
create table `departments` (
`id` int primary key comment 'id',
`parent_id` int comment '父级id',
`name` varchar(100) comment '部门名称',
key `idx_parent_id`(`parent_id`)
) comment '部门表';
部门闭包表
create table `departments_closure_table` (
`ancestor` int comment '祖先',
`descendant` int comment '后代',
`depth` int comment '深度,自己到自己则深度为0',
primary key (`ancestor`, `descendant`, `depth`),
key `idx_ancestor`(`ancestor`),
key `idx_descendant`(`descendant`)
) comment '闭包表';
表初始化
初始化部门表
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (1, '集团总部', NULL);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (2, '华北总部', 1);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (3, '华南总部', 1);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (4, '华东总部', 1);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (5, '华中总部', 1);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (6, '华西总部', 1);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (7, '北京子公司', 2);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (8, '天津子公司', 2);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (9, '河北子公司', 2);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (10, '广东子公司', 3);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (11, '广西子公司', 3);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (12, '海南子公司', 3);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (13, '四川子公司', 6);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (14, '重庆子公司', 6);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (15, '贵州子公司', 6);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (16, '云南子公司', 6);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (17, '成都办事处', 13);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (18, '广元办事处', 13);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (19, '雅安办事处', 13);
INSERT INTO `departments`(`id`, `name`, `parent_id`) VALUES (20, '绵阳办事处', 13);
初始化闭包表
方式一:一层一层初始化
-- 初始化自身关系
INSERT INTO departments_closure_table (ancestor, descendant, depth)
SELECT id, id, 0
FROM departments;
-- 初始化父子关系
INSERT INTO departments_closure_table (ancestor, descendant, depth)
SELECT ct.ancestor, d.id, ct.depth + 1
FROM departments_closure_table AS ct
JOIN departments AS d ON ct.descendant = d.parent_id
where ct.depth + 1 = 1;
-- 初始化爷孙关系
INSERT INTO departments_closure_table (ancestor, descendant, depth)
SELECT ct.ancestor, d.id, ct.depth + 1
FROM departments_closure_table AS ct
JOIN departments AS d ON ct.descendant = d.parent_id
where ct.depth + 1 = 2;
-- 初始化四代关系
INSERT INTO departments_closure_table (ancestor, descendant, depth)
SELECT ct.ancestor, d.id, ct.depth + 1
FROM departments_closure_table AS ct
JOIN departments AS d ON ct.descendant = d.parent_id
where ct.depth + 1 = 3;
方式二:使用 mysql 8.x 数据库递归方式
INSERT INTO departments_closure_table (ancestor, descendant, depth)
WITH RECURSIVE cte AS (
SELECT id as ancestor, id as descendant, 0 as depth
FROM departments
UNION ALL
SELECT cte.ancestor, departments.id, cte.depth + 1
FROM cte
JOIN departments ON cte.descendant = departments.parent_id
)
SELECT ancestor, descendant, depth
FROM cte;
查询
查询所有叶子节点访问路径
SELECT
descendant AS id,
GROUP_CONCAT( ancestor ) AS path
FROM
( SELECT * FROM departments_closure_table ORDER BY depth DESC ) AS ct
WHERE
ct.descendant IN ( SELECT id FROM departments AS t1 WHERE t1.id NOT IN ( SELECT parent_id FROM departments WHERE parent_id IS NOT NULL ) )
GROUP BY
ct.descendant
更新
方式一:全量更新
1、先清除整个闭包表数据
2、重新跑一次初始化比报表
方式二:刷新某个部门以及其子孙部门的闭包关系
1、找出当前部门的直接父级部门
select parent_id from departments where id = 当前部门id
2、找出当前部门 id 以及它的所有子孙部门 id
select descendant from departments_closure_table where ancestor = 当前部门id
3、删除当前部门以及它子孙部门的所有闭包记录
delete from departments_closure_table where descendant in (
select descendant from departments_closure_table where ancestor = 当前部门id
)
4、跑初始化闭包表的 sql ,但要额外加上过滤条件
方式一:一层一层
INSERT INTO departments_closure_table (ancestor, descendant, depth)
SELECT id, id, 0
FROM departments where id in (当前部门id, 它的子孙部门id);
-- 初始化父子关系
INSERT INTO departments_closure_table (ancestor, descendant, depth)
SELECT ct.ancestor, d.id, ct.depth + 1
FROM departments_closure_table AS ct
JOIN departments AS d ON ct.descendant = d.parent_id
where ct.depth + 1 = 1 and d.id in (当前部门id, 它的子孙部门id);
-- 初始化爷孙关系
INSERT INTO departments_closure_table (ancestor, descendant, depth)
SELECT ct.ancestor, d.id, ct.depth + 1
FROM departments_closure_table AS ct
JOIN departments AS d ON ct.descendant = d.parent_id
where ct.depth + 1 = 2 and d.id in (当前部门id, 它的子孙部门id);
-- 初始化四代关系
INSERT INTO departments_closure_table (ancestor, descendant, depth)
SELECT ct.ancestor, d.id, ct.depth + 1
FROM departments_closure_table AS ct
JOIN departments AS d ON ct.descendant = d.parent_id
where ct.depth + 1 = 3 and d.id in (当前部门id, 它的子孙部门id);
方式二:mysql 8.x 递归
INSERT INTO departments_closure_table (ancestor, descendant, depth)
WITH RECURSIVE cte AS (
select * from (
SELECT id as ancestor, id as descendant, 0 as depth FROM departments where id in (当前部门id, 它的子孙部门id)
UNION
select * from departments_closure_table where descendant = 当前部门的直接父级部门id
)
UNION ALL
SELECT cte.ancestor, departments.id, cte.depth + 1
FROM cte
JOIN departments ON cte.descendant = departments.parent_id
where departments.id in (当前部门id, 它的子孙部门id)
)
SELECT ancestor, descendant, depth
FROM cte
where descendant != 当前部门的直接父级部门id
本文是原创文章,采用 CC 4.0 BY-SA 协议,完整转载请注明来自 KK元空间
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果