kal*_*lan 18 sql t-sql sql-server hierarchical-data
我必须在数据库中存储一棵树,那么最好的方法是什么?显示您使用的方法并命名其优缺点.(我使用的是SQL Server 2005)
Cul*_*lub 12
长话短说:
在数据库中存储树或分层结构有三种基本解决方案。
每个解决方案都有优点和缺点:
| 邻接表 | 路径枚举 | 关闭表 | |
|---|---|---|---|
| 仓储费用 | 低:O(n) | 低:O(n) | 高:O(n 2 ) |
| 找到第一个孩子 | 简单/快速 | 简单/快速 | 简单/快速 |
| 找到所有孩子 | 硬/慢 | 简单/中等 | 简单/快速 |
| 寻找直系父母 | 简单/快速 | 简单/快速 | 简单/快速 |
| 查找整个路径 | 硬/中 | 简单/快速 | 简单/快速 |
| 添加孩子 | 简单/快速 | 简单/快速 | 简单/快速 |
| 修改父级 | 简单/快速 | 硬/慢 | 简单/快速 |
| 容易矛盾吗? | 不 | 是的 | 不 |
| 开发复杂度 | 简单的 | 简单的 | 难的 |
CREATE TABLE family_tree_adjacency_list (
PersonId int,
Name varchar(255),
ParentId int
Run Code Online (Sandbox Code Playgroud)
| 人员ID | 姓名 | 父ID |
|---|---|---|
| 1 | 约翰 | 无效的 |
| 2 | 简 | 1 |
| 3 | 标记 | 2 |
| 4 | 玛丽 | 2 |
| 5 | 贝丝 | 4 |
这是SQL中最简单的树结构。易于创建,易于概念化。由于树中的每个节点只有一个父节点,因此您只需存储每个节点的父节点即可存储整个树。任何具有NULL父节点的节点都是顶级节点。如果您不需要查找节点的顶级祖先或底层后代,这可能是一个很好的结构。
优点:
SELECT * FROM family_tree WHERE parent_id = 47)。缺点:
CREATE TABLE family_tree_path_enumeration (
PersonId int,
Name varchar(255),
Path varchar(255)
)
Run Code Online (Sandbox Code Playgroud)
| 人员ID | 姓名 | 小路 |
|---|---|---|
| 1 | 约翰 | 1 |
| 2 | 简 | 1/2 |
| 3 | 标记 | 1/2/3 |
| 4 | 玛丽 | 1/2/4 |
| 5 | 贝丝 | 1/2/4/5 |
这是一个很好的折衷结构。不要过度设计您的解决方案!该解决方案易于实现、易于概念化,并且通常速度相当快。要查找特定节点的所有子节点,您需要使用LIKE通配符%,这比=,但对于大多数情况来说通常没问题。该解决方案唯一真正的缺点是编辑表格。重新组织中间树节点是一项非常复杂的工作,因此如果您经常这样做,请不要使用路径枚举。
优点:
缺点:
LIKE %. 这可能会对一个巨大的数据库产生影响。-- find all sub-children of #1
SELECT * from family_tree_path_enumeration WHERE Path like '%1%
Run Code Online (Sandbox Code Playgroud)
CREATE TABLE family_tree_closure_table (
PersonId int,
Name varchar(255)
)
CREATE TABLE family_tree_relationships (
AncestorId int,
DescendantId int,
Depth int -- this is a helper field to make our lives easier when finding parents and children
)
Run Code Online (Sandbox Code Playgroud)
| 人员ID | 姓名 |
|---|---|
| 1 | 约翰 |
| 2 | 简 |
| 3 | 标记 |
| 4 | 玛丽 |
| 5 | 贝丝 |
| 祖先ID | 后代ID | 深度 |
|---|---|---|
| 1 | 2 | 1 |
| 2 | 3 | 1 |
| 1 | 3 | 2 |
| 2 | 4 | 1 |
| 1 | 4 | 2 |
| 4 | 5 | 1 |
| 2 | 4 | 2 |
| 1 | 4 | 3 |
这个解决方案有点复杂,但即使对于大型数据集它也会很快,并且仍然允许修改而不会遇到问题。这里的缺点是开发稍微复杂一些,并且可以使用指数级更多的存储空间(尽管在实践中通常情况并非如此)。如果您需要查找顶部祖先和底部后代,并且还需要定期编辑树,那么这可能是一个很好的结构。
优点:
缺点:
每个表都有其权衡。如果您想要一个易于重组且易于找到某一级别的父母或孩子的简单解决方案,那么简单的邻接列表会很好。如果您需要查找完整路径(向上或向下),但不需要经常编辑表中间的节点,则路径枚举表会更好。如果您需要查找类别中的所有节点,但还需要能够编辑中间的节点,那么您可能需要使用闭包表。
嗯,最简单的方法是让一条记录有一个 ParentID 列,这样它就知道哪个记录是它的父记录。这是一个非常标准的做法。例如,在线商店可能具有产品类别的层次结构。每个类别都有一个 ParentID。示例:服装数据库中的“牛仔裤”类别可能将“裤子”作为父类别。如果你想要一个记录表明哪些是它的孩子,这有点困难,除非你限制孩子的数量。如果你想要一个二叉树,你可以有 LeftChildID 和 RightChildID 列。如果您允许任意数量的子项,则可以有一个子项列,其中 ID 以逗号分隔(例如1,4,72,19) 但这会使查询变得相当困难。如果您的数据库允许列中的数组类型,您可能可以使用数组而不是分隔字符串,这很容易查询 - 但我不确定 MS SQL Server 是否支持。
除此之外,这取决于您正在建模的数据类型,以及您计划对这棵树执行的操作类型。
| 归档时间: |
|
| 查看次数: |
13890 次 |
| 最近记录: |