你在数据库中建模和检索层级信息的方式是什么?
我喜欢修改过的先序遍历树算法。 这种技术使得对树进行查询非常容易。
这是从Zend Framework(PHP)贡献者网页(Laurent Melmoux于2007年6月05日15:52发布)上复制的有关该主题的链接列表。
其中许多链接是与语言无关的:
有两种主要的表示和用数据库表示分层结构的算法:
这在这里很好地解释了:
这是我收集的一些其他链接:
嵌套集
图形
课程:
嵌套集合数据库树 Adodb
访问模型ADODb
PEAR::DB_NestedSet
PEAR::Tree
nstrees
在这个主题上具有决定性作用的文章是由乔·塞尔科(Joe Celko)撰写的,他将其中一些文章整理成了一本名为《Joe Celko's Trees and Hierarchies in SQL for Smarties》的书。
他青睐一种叫做有向图的技术。有关他在这个主题上的工作介绍可以在这里找到。
如何在SQL数据库中表示层次结构?有没有一种通用的、可移植的技术呢?假设层次结构大多是只读的,但并非完全静态。比如说家谱。
以下是不应该采用的方式:
create table person (
person_id integer autoincrement primary key,
name varchar(255) not null,
dob date,
mother integer,
father integer
);
像这样插入数据:
person_id name dob mother father
1 Pops 1900/1/1 null null
2 Grandma 1903/2/4 null null
3 Dad 1925/4/2 2 1
4 Uncle Kev 1927/3/3 2 1
5 Cuz Dave 1953/7/8 null 4
6 Billy 1954/8/1 null 3
相反,将节点和关系分别拆分为两个表。
create table person (
person_id integer autoincrement primary key,
name varchar(255) not null,
dob date
);
create table ancestor (
ancestor_id integer,
descendant_id integer,
distance integer
);
数据是这样创建的:
person_id name dob
1 Pops 1900/1/1
2 Grandma 1903/2/4
3 Dad 1925/4/2
4 Uncle Kev 1927/3/3
5 Cuz Dave 1953/7/8
6 Billy 1954/8/1
ancestor_id descendant_id distance
1 1 0
2 2 0
3 3 0
4 4 0
5 5 0
6 6 0
1 3 1
2 3 1
1 4 1
2 4 1
1 5 2
2 5 2
4 5 1
1 6 2
2 6 2
3 6 1
现在,您可以运行任意查询,而不需要将表重新连接回自身,如果您在同一行中具有层次关系,则会发生这种情况。
谁有祖父母?
select * from person where person_id in
(select descendant_id from ancestor where distance=2);
所有子元素:
select * from person where person_id in
(select descendant_id from ancestor
where ancestor_id=1 and distance>0);
叔叔是谁?
select decendant_id uncle from ancestor
where distance=1 and ancestor_id in
(select ancestor_id from ancestor
where distance=2 and not exists
(select ancestor_id from ancestor
where distance=1 and ancestor_id=uncle)
)
通过子查询连接自身表格可能会遇到很多问题,而常见的限制是只有16个子查询。
然而,维护祖先表格相对较难,最好使用存储过程完成。
我不同意Josh的观点。如果你正在使用一个大型的分层结构,比如公司组织,人们可能会加入/离开公司,改变汇报线等等……这样就很难维护“距离”,你还需要维护两张数据表。
以下查询(适用于SQL Server 2005及以上版本)可以让你看到任何人的完整行并计算他们在层级中的位置,并且它只需要一张用户信息表。它可以被修改以查找任何子关系。
--Create table of dummy data
create table #person (
personID integer IDENTITY(1,1) NOT NULL,
name varchar(255) not null,
dob date,
father integer
);
INSERT INTO #person(name,dob,father)Values('Pops','1900/1/1',NULL);
INSERT INTO #person(name,dob,father)Values('Grandma','1903/2/4',null);
INSERT INTO #person(name,dob,father)Values('Dad','1925/4/2',1);
INSERT INTO #person(name,dob,father)Values('Uncle Kev','1927/3/3',1);
INSERT INTO #person(name,dob,father)Values('Cuz Dave','1953/7/8',4);
INSERT INTO #person(name,dob,father)Values('Billy','1954/8/1',3);
DECLARE @OldestPerson INT;
SET @OldestPerson = 1; -- Set this value to the ID of the oldest person in the family
WITH PersonHierarchy (personID,Name,dob,father, HierarchyLevel) AS
(
SELECT
personID
,Name
,dob
,father,
1 as HierarchyLevel
FROM #person
WHERE personID = @OldestPerson
UNION ALL
SELECT
e.personID,
e.Name,
e.dob,
e.father,
eh.HierarchyLevel + 1 AS HierarchyLevel
FROM #person e
INNER JOIN PersonHierarchy eh ON
e.father = eh.personID
)
SELECT *
FROM PersonHierarchy
ORDER BY HierarchyLevel, father;
DROP TABLE #person;
Oracle: SELECT ... START WITH ... CONNECT BY
Oracle有一个扩展功能可以方便地进行基于树状结构的检索。也许SQL Server也有类似的扩展功能?
这个查询将遍历一个表,其中嵌套关系存储在parent和child列中。
select * from my_table
start with parent = :TOP
connect by prior child = parent;