您在数据库中建模和检索分层信息的方法有哪些?
答案 0 :(得分:31)
我喜欢Modified Preorder Tree Traversal算法。这种技术使查询树变得非常容易。
但是这里有一个关于我从Zend Framework(PHP)贡献者网页上复制的主题的链接列表(由Laurent Melmoux发布于2007年6月5日15:52发布)。
许多链接都与语言无关:
有两种主要的表示和算法来表示具有数据库的层次结构:
这里有很好的解释:
以下是我收集的一些链接:
邻接列表模型
嵌套集
Graphes
课程:
嵌套集DB树Adodb
访问模型ADOdb
PEAR :: DB_NestedSet
PEAR ::树
nstrees
答案 1 :(得分:14)
关于这个主题的最终部分是由Joe Celko编写的,他已经将其中的一些作品编写成了一本名为Joe Celko的树和层次结构的SQL for Smarties。
他赞成一种称为有向图的技术。可以找到他关于这一主题的工作的介绍here
答案 2 :(得分:10)
在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
现在,您可以运行不涉及将表连接回自身的仲裁查询,如果您在与节点相同的行中具有heirachy关系,则会发生这种情况。
谁有祖父母?
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个子查询。
麻烦的是,维护祖先表有点困难 - 最好用存储过程完成。
答案 3 :(得分:9)
我不同意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;
答案 4 :(得分:4)
仅供参考:SQL Server 2008为此类情况引入了新的HierarchyID数据类型。使您可以控制行中“树”的位置,水平和垂直。
答案 5 :(得分:3)
Oracle:SELECT ... START WITH ... CONNECT BY
Oracle对SELECT有一个扩展,允许基于树的检索。也许SQL Server有一些类似的扩展名?
此查询将遍历一个表,其中嵌套关系存储在父和子列中。
select * from my_table
start with parent = :TOP
connect by prior child = parent;
答案 6 :(得分:2)
我更喜欢混合使用Josh和Mark Harrison的技术:
两个表,一个包含Person的数据,另一个包含层次结构信息(person_id,parent_id [,mother_id])如果此表的PK是person_id,则您有一个只有一个父节点的简单树(其中在这种情况下是有道理的,但在会计账户等其他情况下却没有意义
这个hiarchy表可以通过递归过程进行遍历,或者如果你的数据库通过SELECT ... BY PRIOR(Oracle)这样的句子来支持它。
其他可能性是,如果您知道要保留的层次结构数据的最大深度是使用单个表,每个层次结构具有一组列
答案 7 :(得分:1)
当我们为[fleXive]实现树组件并使用了来自MySQL文档的tharkun提到的嵌套集树模型方法时,我们遇到了同样的问题。
除了速度提升(显着)之外,我们还使用了 spreaded 方法,这意味着我们使用了最高级别的最大Long值来允许我们插入和移动节点而无需重新计算所有左右值。通过将节点的范围除以3来计算左和右的值,并使用内部第三个作为新节点的边界。
可以看到一个java代码示例here。
答案 8 :(得分:0)
如果您使用的是SQL Server 2005,则this link会解释如何检索分层数据。
普通表格表达式(CTE)一旦您习惯使用它们就可以成为您的朋友。