MySQL 递归查询
MySQL 归查询
前言
最近在做的业务场景涉及到了数据库的递归查询。我们公司用的 Oracle ,众所周知,Oracle 自带有递归查询的功能,所以实现起来特别简单。
但是,我记得 MySQL 是没有递归查询功能的,那 MySQL 中应该怎么实现呢?
于是,就有了这篇文章。
文章主要知识点:
- Oracle 递归查询, start with connect by prior 用法
- find_in_set 函数
- concat,concat_ws,group_concat 函数
- MySQL 自定义函数
- 手动实现 MySQL 递归查询
Oracle 递归查询
在 Oracle 中是通过 start with connect by prior 语法来实现递归查询的。
按照 prior 关键字在子节点端还是父节点端,以及是否包含当前查询的节点,共分为四种情况。
prior 在子节点端(向下递归)
第一种情况:start with 子节点id = ' 查询节点 ' connect by prior 子节点id = 父节点id
select * from dept start with id='1001' connet by prior id=pid;
这里,按照条件 id='1001' 对当前节点以及它的子节点递归查询。查询结果包含自己及所有子节点。
第二种情况:start with 父节点id= ' 查询节点 ' connect by prior 子节点id = 父节点 id
select * from dept start with pid='1001' connect by prior id=pid;
这里,按照条件 pid='1001' 对当前节点的所有子节点递归查询。查询结果只包含它的所有子节点,不包含自己。
其实想一想也对,因为开始条件是以父节点为根节点,且向下递归,自然不包含当前节点。
prior 在父节点端(向上递归)
第三种情况:start with 子节点id= ' 查询节点 ' connect by prior 父节点id = 子节点id
select * from dept start with id='1001' connect by prior pid=id;
这里按照条件 id='1001' ,对当前节点及其父节点递归查询。查询结果包括自己及其所有父节点。
第四种情况:start with 父节点id= ' 查询节点 ' connect by prior 父节点id = 子节点id
select * from dept start with pid='1001' connect by prior pid=id;
这里按照条件 pid='1001',对当前节点的第一代子节点以及它的父节点递归查询。查询结果包括自己的第一代子节点以及所有父节点。(包括自己)
其实这种情况也好理解,因为查询开始条件是以 父节点
为根节点,且向上递归,自然需要把当前父节点的第一层子节点包括在内。
以上四种情况初看可能会让人迷惑,容易记混乱,其实不然。
我们只需要记住 prior 的位置在子节点端,就向下递归,在父节点端就向上递归。
- 开始条件若是子节点的话,自然包括它本身的节点。
- 开始条件若是父节点的话,则向下递归时,自然不包括当前节点。而向上递归,需要包括当前节点及其第一代子节点。
MySQL 递归查询
可以看到,Oracle 实现递归查询非常的方便。但是,在 MySQL 中并没有帮我们处理,因此需要我们自己手动实现递归查询。
为了方便,我们创建一个部门表,并插入几条可以形成递归关系的数据。
DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (
`id` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`pid` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `dept`(`id`, `name`, `pid`) VALUES ('1000', '总公司', NULL);
INSERT INTO `dept`(`id`, `name`, `pid`) VALUES ('1001', '北京分公司', '1000');
INSERT INTO `dept`(`id`, `name`, `pid`) VALUES ('1002', '上海分公司', '1000');
INSERT INTO `dept`(`id`, `name`, `pid`) VALUES ('1003', '北京研发部', '1001');
INSERT INTO `dept`(`id`, `name`, `pid`) VALUES ('1004', '北京财务部', '1001');
INSERT INTO