【原创】MySQL 模拟Oracle邻接模型树形处理

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介:

数据库对层次结构的处理模型有好多种,可以根据自己的需求来设计模型,当然最简单的也是最容易设计的模型就是所谓的邻接模型。在这方面,其他数据库比如Oracle 提供了现成的分析方法 connect by,而MySQL在这方面就显得有些薄弱了。 不过可以用MySQL的存储过程实现ORACLE类似的分析功能


这样,先来创建一个简单的数表。

1
2
create  table  country ( id number(2)  not  null ,   name  varchar (60)  not  null );
create  table  country_relation (id number(2),  parentid number(2));


插入一些数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- Table country.
insert  into  country (id, name values  (0, 'Earth' );
insert  into  country (id, name values  (2, 'North America' );
insert  into  country (id, name values  (3, 'South America' );
insert  into  country (id, name values  (4, 'Europe' );
insert  into  country (id, name values  (5, 'Asia' );
insert  into  country (id, name values  (6, 'Africa' );
insert  into  country (id, name values  (7, 'Australia' );
insert  into  country (id, name values  (8, 'Canada' );
insert  into  country (id, name values  (9, 'Central America' );
insert  into  country (id, name values  (10, 'Island Nations' );
insert  into  country (id, name values  (11, 'United States' );
insert  into  country (id, name values  (12, 'Alabama' );
insert  into  country (id, name values  (13, 'Alaska' );
insert  into  country (id, name values  (14, 'Arizona' );
insert  into  country (id, name values  (15, 'Arkansas' );
insert  into  country (id, name values  (16, 'California' );
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
-- Table country_relation.
insert  into  country_relation (id,parentid)  values  (0, NULL );
insert  into  country_relation (id,parentid)  values  (2,0);
insert  into  country_relation (id,parentid)  values  (3,0);
insert  into  country_relation (id,parentid)  values  (4,0);
insert  into  country_relation (id,parentid)  values  (5,0);
insert  into  country_relation (id,parentid)  values  (6,0);
insert  into  country_relation (id,parentid)  values  (7,0);
insert  into  country_relation (id,parentid)  values  (8,2);
insert  into  country_relation (id,parentid)  values  (9,2);
insert  into  country_relation (id,parentid)  values  (10,2);
insert  into  country_relation (id,parentid)  values  (11,2);
insert  into  country_relation (id,parentid)  values  (12,11);
insert  into  country_relation (id,parentid)  values  (13,11);
insert  into  country_relation (id,parentid)  values  (14,11);
insert  into  country_relation (id,parentid)  values  (15,11);
insert  into  country_relation (id,parentid)  values  (16,11);



在Oracle 里面,对这些操作就比较简单了,都是系统提供的。

比如下面四种情形:

1). 查看深度,

1
2
3
4
5
6
7
select  max ( level "level"  from  COUNTRY_RELATION a start  with  a.parentid  is  NULL
connect  by  PRIOR  a.id = a.PARENTID
order  by  level ;
      level
----------
          4
已用时间:  00: 00: 00.03

2). 查看叶子节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
select  name  from 
(
select  b. name , connect_by_isleaf  "isleaf"
from  COUNTRY_RELATION a  inner  join  country b  on  (a.id = b.id) 
start  with  a.parentid  is  NULL  connect  by  prior  a.id = a.PARENTID 
) T  where  T. "isleaf"  = 1;
NAME
--------------------------------------------------
Canada
Central America
Island Nations
Alabama
Alaska
Arizona
Arkansas
California
South America
Europe
Asia
Africa
Australia
已选择13行。
已用时间:  00: 00: 00.01


3) 查看ROOT节点

1
2
3
4
5
6
7
select  connect_by_root b. name
from  COUNTRY_RELATION a  inner  join  country b  on  (a.id = b.id) 
start  with  a.parentid  is  NULL  connect  by  a.id = a.PARENTID 
CONNECT_BY_ROOTB. NAME
--------------------------------------------------
Earth
已用时间:  00: 00: 00.01

4). 查看路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
select  sys_connect_by_path(b. name , '/' "path" 
from  COUNTRY_RELATION a  inner  join  country b  on  (a.id = b.id) 
start  with  a.parentid  is  NULL  connect  by  prior  a.id = a.PARENTID 
order  by  level ,a.id;
path
--------------------------------------------------
/Earth
/Earth/North America
/Earth/South America
/Earth/Europe
/Earth/Asia
/Earth/Africa
/Earth/Australia
/Earth/North America/Canada
/Earth/North America/Central America
/Earth/North America/Island Nations
/Earth/North America/United States
/Earth/North America/United States/Alabama
/Earth/North America/United States/Alaska
/Earth/North America/United States/Arizona
/Earth/North America/United States/Arkansas
/Earth/North America/United States/California
已选择16行。
已用时间:  00: 00: 00.01



接下来我们看看在MySQL 里面如何实现上面四种情形:

前三种都比较简单,可以很容易写出SQL。

1)查看深度

1
2
3
4
5
6
7
8
mysql>  SELECT  COUNT ( DISTINCT  IFNULL(parentid,-1))  AS  LEVEL  FROM  country_relation
;
+ -------+
LEVEL  |
+ -------+
|     4 |
+ -------+
1 row  in  set  (0.00 sec

)


2)查看ROOT节点

1
2
3
4
5
6
7
8
9
10
mysql>  SELECT  b.` name AS  root_node  FROM
     -> (
     ->  SELECT   id  FROM  country_relation  WHERE  parentid  IS  NULL
     -> )  AS  a, country  AS  WHERE  a.id = b.id;
+ -----------+
| root_node |
+ -----------+
| Earth     |
+ -----------+
1 row  in  set  (0.00 sec)


3).  查看叶子节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysql>  SELECT  b.` name AS  leaf_node  FROM
     -> (
     ->  SELECT   id  FROM  country_relation  WHERE  id  NOT  IN  ( SELECT  IFNULL(parentid,
-1)  FROM  country_relation)
     -> )  AS  a, country  AS  WHERE  a.id = b.id;
+ -----------------+
| leaf_node       |
+ -----------------+
| South America   |
| Europe          |
| Asia            |
| Africa          |
| Australia       |
| Canada          |
| Central America |
| Island Nations  |
| Alabama         |
| Alaska          |
| Arizona         |
| Arkansas        |
| California      |
+ -----------------+
13  rows  in  set  (0.00 sec)
mysql>


4) 查看路径

这一块没有简单的SQL实现,不过可以用MySQL的存储过程来实现同样的功能。

存储过程代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
DELIMITER $$
USE `t_girl`$$
DROP  PROCEDURE  IF EXISTS `sp_show_list`$$
CREATE  DEFINER=`root`@`localhost`  PROCEDURE  `sp_show_list`()
BEGIN
       -- Created by ytt 2014/11/04.
       -- Is equal to oracle's connect by syntax.
       -- Body.
       DROP  TABLE  IF EXISTS tmp_country_list;
       CREATE  TEMPORARY  TABLE  tmp_country_list (node_level  INT  UNSIGNED   NOT  NULL , node_path  VARCHAR (1000)  NOT  NULL );
       -- Get the root node.
       INSERT  INTO  tmp_country_list   SELECT  1, CONCAT('/ ',id) FROM country_relation WHERE parentid IS NULL;
       -- Loop within all parent node.
       cursor1:BEGIN
         DECLARE done1 INT DEFAULT 0;
         DECLARE i1 INT DEFAULT 1;
         DECLARE v_parentid INT DEFAULT -1;
         DECLARE v_node_path VARCHAR(1000) DEFAULT ' ';
         DECLARE cr1 CURSOR FOR SELECT  parentid FROM country_relation WHERE parentid IS NOT NULL GROUP BY parentid ORDER BY parentid ASC;
         DECLARE CONTINUE HANDLER FOR NOT FOUND SET done1 = 1;
         
         OPEN cr1;
         
         loop1:LOOP
           FETCH cr1 INTO v_parentid;
           IF done1 = 1 THEN 
             LEAVE loop1;
           END IF;
           SET i1 = i1 + 1;
           
           label_path:BEGIN
             DECLARE done2 INT DEFAULT 0;
             DECLARE CONTINUE HANDLER FOR NOT FOUND SET done2 = 1;
             -- Get the upper path.
             SELECT node_path FROM tmp_country_list WHERE node_level = i1 - 1  AND LOCATE(v_parentid,node_path) > 0 INTO v_node_path;
             -- Escape the outer not found exception.
             IF done2 = 1 THEN
               SET done2 = 0;
             END IF;
             INSERT INTO tmp_country_list
             SELECT i1,CONCAT(IFNULL(v_node_path,' '),' / ',id) FROM country_relation WHERE parentid = v_parentid;
           END;
         END LOOP;
         
         CLOSE cr1;
         
       END;
       -- Update node' s id  to  its  real  name .
       update_name_label: BEGIN
         DECLARE  cnt  INT  DEFAULT  0;
         DECLARE  i2  INT  DEFAULT  0;
         SELECT  MAX (node_level)  FROM  tmp_country_list  INTO  cnt;
         WHILE i2 < cnt
         DO
           UPDATE  tmp_country_list  AS  a, country  AS 
           SET  a.node_path =  REPLACE (a.node_path,CONCAT( '/' ,b.id),CONCAT( '/' ,b. name ))
           WHERE   LOCATE(CONCAT( '/' ,b.id),a.node_path) > 0;
           SET  i2 = i2 + 1;
         END  WHILE;
       END ;
      
      SELECT  node_path  FROM  tmp_country_list;
     END $$
DELIMITER ;


调用结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
mysql> CALL sp_show_list();
+ -----------------------------------------------+
| node_path                                     |
+ -----------------------------------------------+
| /Earth                                        |
| /Earth/North America                          |
| /Earth/South America                          |
| /Earth/Europe                                 |
| /Earth/Asia                                   |
| /Earth/Africa                                 |
| /Earth/Australia                              |
| /Earth/North America/Canada                   |
| /Earth/North America/Central America          |
| /Earth/North America/Island Nations           |
| /Earth/North America/United States            |
| /Earth/North America/United States/Alabama    |
| /Earth/North America/United States/Alaska     |
| /Earth/North America/United States/Arizona    |
| /Earth/North America/United States/Arkansas   |
| /Earth/North America/United States/California |
+ -----------------------------------------------+
16  rows  in  set  (0.04 sec)
Query OK, 0  rows  affected (0.08 sec)
mysql>





本文转自 david_yeung 51CTO博客,原文链接:http://blog.51cto.com/yueliangdao0608/1571772 ,如需转载请自行联系原作者
相关实践学习
如何快速连接云数据库RDS MySQL
本场景介绍如何通过阿里云数据管理服务DMS快速连接云数据库RDS MySQL,然后进行数据表的CRUD操作。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
11天前
|
SQL Oracle 关系型数据库
MySQL 和 Oracle 的区别?
本文对比了Oracle和MySQL数据库的多个方面。Oracle适用于大型数据库,支持高并发和大访问量,市场占有率为40%,安装占用空间较大,约3G;而MySQL适合中小型应用,是开源免费的,安装仅需152M。两者在主键生成、字符串处理、SQL语句、事务处理等方面存在差异。Oracle功能更为强大,尤其在企业级应用中表现突出,而MySQL则以简单易用见长。
MySQL 和 Oracle 的区别?
|
19天前
|
存储 缓存 关系型数据库
MySQL底层概述—3.InnoDB线程模型
InnoDB存储引擎采用多线程模型,包含多个后台线程以处理不同任务。主要线程包括:IO Thread负责读写数据页和日志;Purge Thread回收已提交事务的undo日志;Page Cleaner Thread刷新脏页并清理redo日志;Master Thread调度其他线程,定时刷新脏页、回收undo日志、写入redo日志和合并写缓冲。各线程协同工作,确保数据一致性和高效性能。
MySQL底层概述—3.InnoDB线程模型
|
10天前
|
存储 关系型数据库 MySQL
MySQL进阶突击系列(09)数据磁盘存储模型 | 一行数据怎么存?
文中详细介绍了MySQL数据库中一行数据在磁盘上的存储机制,包括表空间、段、区、页和行的具体结构,以及如何设计和优化行数据存储以提高性能。
|
23天前
|
存储 缓存 关系型数据库
MySQL原理简介—5.存储模型和数据读写机制
本文介绍了MySQL中InnoDB存储引擎的物理存储结构和读写机制。主要内容包括: 1. 为什么不能直接更新磁盘上的数据 2. 数据页的概念 3. 一行数据的存储 4. 数据头的内容 5. 行溢出和溢出页 6. 数据页的物理结构 7. 表空间的物理结构 8. InnoDB存储模型及读写机制总结 这些机制共同确保了InnoDB在高并发场景下的高效运行和数据一致性。
|
2月前
|
监控 Oracle 关系型数据库
Mysql、Oracle审计日志的开启
通过上述步骤,可以在 MySQL 和 Oracle 数据库中启用和配置审计日志。这些日志对于监控数据库操作、提高安全性和满足合规性要求非常重要。确保正确配置审计参数和策略,定期查看和分析审计日志,有助于及时发现并处理潜在的安全问题。
66 11
|
6月前
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
1094 18
|
6月前
|
Oracle NoSQL 关系型数据库
主流数据库对比:MySQL、PostgreSQL、Oracle和Redis的优缺点分析
主流数据库对比:MySQL、PostgreSQL、Oracle和Redis的优缺点分析
1070 2
|
5月前
|
Oracle 关系型数据库 MySQL
shell获取多个oracle库mysql库所有的表
请注意,此脚本假设你有足够的权限访问所有提到的数据库。在实际部署前,请确保对脚本中的数据库凭据、主机名和端口进行适当的修改和验证。此外,处理数据库操作时,务必谨慎操作,避免因错误的脚本执行造成数据损坏或服务中断。
64 0
|
7月前
|
Oracle 关系型数据库 MySQL
Mysql和Oracle数据库死锁查看以及解决
【8月更文挑战第11天】本文介绍了解决MySQL与Oracle数据库死锁的方法。MySQL可通过`SHOW ENGINE INNODB STATUS`查看死锁详情,并自动回滚一个事务解除死锁;也可手动KILL事务。Oracle则通过查询V$LOCK与V$SESSION视图定位死锁,并用`ALTER SYSTEM KILL SESSION`命令终止相关会话。预防措施包括遵循ACID原则、优化索引及拆分大型事务。
516 3
|
2月前
|
关系型数据库 MySQL 数据库连接
数据库连接工具连接mysql提示:“Host ‘172.23.0.1‘ is not allowed to connect to this MySQL server“
docker-compose部署mysql8服务后,连接时提示不允许连接问题解决

推荐镜像

更多