十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
树形结构的存储是一种非常典型的需求,例如菜单、省市区、栏目等等。
网站建设哪家好,找成都创新互联!专注于网页设计、网站建设、微信开发、微信小程序开发、集团企业网站建设等服务项目。为回馈新老客户创新互联还提供了精河免费建站欢迎大家使用!
将树形结构的每个节点作为一行存储,每个节点保存父节点的指针(pid)。优点是简单易懂,插入修改比较简单。缺点是若要获取某个节点的所有子节点,将是一件非常恶心的事情。
在方式1中增加left和right,相当于btree的左右分支,分别存储左右分支节点的最大值和最小值。优点是查询一个节点的子节点容易,仅需做范围查询查询即可。缺点是由于树形结构存储在里面,增加或修改已存在的节点将可能产生连锁反应,操作复杂。
将整个树结构存成一个文档,文档结构即树形结构,简明易懂。缺点是文档越来越大,对所有节点的修改都集中在此文档中,并发操作受限。
将每个节点的子节点保存起来,优点是结构简单查询子节点方便,缺点是查询父节点会表麻烦。
充分利用文档型存储 schema-less的优点,受限存储一个大的树形文档,再将每个节点的其他信息单独存储。优点是操作简单,结构上的操作可直接操作树形文档,数据上的操作仅需操作单条数据。缺点,对所有节点的修改都集中在此文档中,并发操作受限。
主要是要有ID,PID两个字段,下面是我用的一个表,仅供参考:
CREATE TABLE [dbo].[Sys_Menu](
[ID] [int] NOT NULL,
[Code] [varchar](50) NOT NULL,
[PID] [int] NOT NULL,
[Name] [nvarchar](50) NULL,
[Url] [varchar](100) NULL,
[STATE] [bit] NOT NULL,
[IsSelected] [bit] NULL
) ON [PRIMARY]
GO
在 MySQL 表中存储树形结构数据:
一般比较普遍的就是四种方法:(具体见 SQL Anti-patterns这本书)
Adjacency List:每一条记录存parent_id
Path Enumerations:每一条记录存整个tree path经过的node枚举
Nested Sets:每一条记录存 nleft 和 nright
Closure Table:维护一个表,所有的tree path作为记录进行保存。
树形结构统一使用下面的测试表与测试数据
CREATE TABLE test_tree (
test_id INT,
pid INT,
test_val VARCHAR(10),
PRIMARY KEY (test_id)
);
INSERT INTO test_tree VALUES(1, NULL, '.NET');
INSERT INTO test_tree VALUES(2, 1, 'C#');
INSERT INTO test_tree VALUES(3, 1, 'J#');
INSERT INTO test_tree VALUES(4, 1, 'ASP.NET');
INSERT INTO test_tree VALUES(5, 1, 'VB.NET');
INSERT INTO test_tree VALUES(6, NULL, 'J2EE');
INSERT INTO test_tree VALUES(7, 6, 'EJB');
INSERT INTO test_tree VALUES(8, 6, 'Servlet');
INSERT INTO test_tree VALUES(9, 6, 'JSP');
INSERT INTO test_tree VALUES(10, NULL, 'Database');
INSERT INTO test_tree VALUES(11, 10, 'DB2');
INSERT INTO test_tree VALUES(12, 10, 'MySQL');
INSERT INTO test_tree VALUES(13, 10, 'Oracle');
INSERT INTO test_tree VALUES(14, 10, 'SQL Server');
INSERT INTO test_tree VALUES(15, 13, 'PL/SQL');
INSERT INTO test_tree VALUES(16, 15, 'Function');
INSERT INTO test_tree VALUES(17, 15, 'Procedure');
INSERT INTO test_tree VALUES(18, 15, 'Package');
INSERT INTO test_tree VALUES(19, 15, 'Cursor');
INSERT INTO test_tree VALUES(20, 14, 'T-SQL');
Oracle
使用 START WITH CONNECT BY
语句实现树状查询
SQL ed
Wrote file afiedt.buf
1 SELECT
2 LPAD(' ', 2*(LEVEL-1)) || test_val AS test_val
3 FROM
4 test_tree
5 START WITH
6 test_id IN (1, 6, 10)
7* CONNECT BY PRIOR test_id = pid
SQL /
TEST_VAL
-----------------------------------------------------------
.NET
C#
J#
ASP.NET
VB.NET
J2EE
EJB
Servlet
JSP
Database
DB2
TEST_VAL
-----------------------------------------------------------
MySQL
Oracle
PL/SQL
Function
Procedure
Package
Cursor
SQL Server
T-SQL
20 rows selected.
SQL Server
使用 Common Table Expression (CTE) 来实现 递归调用。
1 WITH StepCTE
2 AS
3 (
4 SELECT
5 test_id,
6 pid,
7 test_val,
8 1 as Lev
9 FROM
10 test_tree
11 WHERE
12 test_id IN (1,6,10)
13 UNION ALL
14 SELECT
15 T.test_id,
16 T.pid,
17 T.test_val,
18 CTE.Lev + 1
19 FROM
20 test_tree T INNER JOIN StepCTE CTE
21 ON T.pid = CTE.test_id
22 )
23 SELECT
24 test_id, pid, test_val, Lev
25 FROM StepCTE;
26 go
test_id pid test_val Lev
----------- ----------- ---------- -----------
1 NULL .NET 1
6 NULL J2EE 1
10 NULL Database 1
11 10 DB2 2
12 10 MySQL 2
13 10 Oracle 2
14 10 SQL Server 2
20 14 T-SQL 3
15 13 PL/SQL 3
16 15 Function 4
17 15 Procedure 4
18 15 Package 4
19 15 Cursor 4
7 6 EJB 2
8 6 Servlet 2
9 6 JSP 2
2 1 C# 2
3 1 J# 2
4 1 ASP.NET 2
5 1 VB.NET 2
(20 行受影响)