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

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 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 ,如需转载请自行联系原作者
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
8月前
|
Oracle 关系型数据库 MySQL
Oracle linux 8 二进制安装 MySQL 8.4企业版
Oracle linux 8 二进制安装 MySQL 8.4企业版
312 1
|
10月前
|
SQL Oracle 关系型数据库
MySQL 和 Oracle 的区别?
本文对比了Oracle和MySQL数据库的多个方面。Oracle适用于大型数据库,支持高并发和大访问量,市场占有率为40%,安装占用空间较大,约3G;而MySQL适合中小型应用,是开源免费的,安装仅需152M。两者在主键生成、字符串处理、SQL语句、事务处理等方面存在差异。Oracle功能更为强大,尤其在企业级应用中表现突出,而MySQL则以简单易用见长。
1230 7
MySQL 和 Oracle 的区别?
|
9月前
|
Oracle 关系型数据库 MySQL
使用崖山YMP 迁移 Oracle/MySQL 至YashanDB 23.2 验证测试
这篇文章是作者尚雷关于使用崖山YMP迁移Oracle/MySQL至YashanDB 23.2的验证测试分享。介绍了YMP的产品信息,包括架构、版本支持等,还详细阐述了外置库部署、YMP部署、访问YMP、数据源管理、任务管理(创建任务、迁移配置、离线迁移、校验初始化、一致性校验)及MySQL迁移的全过程。
|
10月前
|
存储 关系型数据库 MySQL
MySQL进阶突击系列(09)数据磁盘存储模型 | 一行数据怎么存?
文中详细介绍了MySQL数据库中一行数据在磁盘上的存储机制,包括表空间、段、区、页和行的具体结构,以及如何设计和优化行数据存储以提高性能。
|
11月前
|
监控 Oracle 关系型数据库
Mysql、Oracle审计日志的开启
通过上述步骤,可以在 MySQL 和 Oracle 数据库中启用和配置审计日志。这些日志对于监控数据库操作、提高安全性和满足合规性要求非常重要。确保正确配置审计参数和策略,定期查看和分析审计日志,有助于及时发现并处理潜在的安全问题。
676 11
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
2280 18
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
Oracle NoSQL 关系型数据库
主流数据库对比:MySQL、PostgreSQL、Oracle和Redis的优缺点分析
主流数据库对比:MySQL、PostgreSQL、Oracle和Redis的优缺点分析
2555 3
|
Oracle 关系型数据库 MySQL
shell获取多个oracle库mysql库所有的表
请注意,此脚本假设你有足够的权限访问所有提到的数据库。在实际部署前,请确保对脚本中的数据库凭据、主机名和端口进行适当的修改和验证。此外,处理数据库操作时,务必谨慎操作,避免因错误的脚本执行造成数据损坏或服务中断。
203 0
|
3月前
|
缓存 关系型数据库 BI
使用MYSQL Report分析数据库性能(下)
使用MYSQL Report分析数据库性能
155 3
|
3月前
|
关系型数据库 MySQL 数据库
自建数据库如何迁移至RDS MySQL实例
数据库迁移是一项复杂且耗时的工程,需考虑数据安全、完整性及业务中断影响。使用阿里云数据传输服务DTS,可快速、平滑完成迁移任务,将应用停机时间降至分钟级。您还可通过全量备份自建数据库并恢复至RDS MySQL实例,实现间接迁移上云。

热门文章

最新文章

推荐镜像

更多