例如,“都乐香蕉”是一种产品,它列在“香蕉”类别下,当我打开“水果”类别时,我想看到“都乐香蕉”。
+ Food
|--+ Fruits
|------+ Bananas
|------+ Apples
|--+ Vegetables
|------+ Onion
|------+ Spinach
例如,“都乐香蕉”是一种产品,它列在“香蕉”类别下,当我打开“水果”类别时,我想看到“都乐香蕉”。
+ Food
|--+ Fruits
|------+ Bananas
|------+ Apples
|--+ Vegetables
|------+ Onion
|------+ Spinach
如果您正在寻找解决此问题的在线资源,“将树存储在数据库中”将是一个很好的搜索词组。
至于解决方案,请注意每个子类别可以有一个或零个父类别。因此,整个树可以存储在具有“父”字段的单个自引用表中。
使用您的示例树:
ID | PARENT | NAME
-----+--------+-------------
1 | null | Food
2 | 1 | Fruits
3 | 2 | Bananas
4 | 2 | Apples
5 | 1 | Vegetables
6 | 5 | Onion
7 | 5 | Spinach
我通常使用非常适合数据库查询的左右树。每个节点都有一个 parentId、left 和 right 值。每个节点子节点都有一个左/右值,它位于父节点左右之间,这使得查找节点的所有子节点/父节点变得非常容易。它确实给插入带来了轻微的开销,但除非你插入很多,否则它不应该有太大的影响。
编辑:不过,只是一个警告,您需要在锁定的事务中进行插入/更新操作,否则树可能会搞砸。
具有 3 个字段的表“类别”。
获取所有根类别
select * from Categories where ParentCategoryId is null
要获取某个特定类别的所有子类别:
select * from Categories where ParentCategoryId = 12
您可以使用带有 parent_category_id 的简单表结构并使用递归检索整个树或实现左/右值并使用预排序树遍历方法获取整个树。
如果您的意思是无限数量的级别,那么可以递归的自引用表。示例:StuffID、StuffName、StuffParentID(FK 到 Stuff ID)
对于有限数量的固定表:父子孙子
CREATE TABLE [dbo].[Category](
[CategoryId] [int] NOT NULL,
[ParentCategoryId] [int] NULL,
[CategoryName] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Category] PRIMARY KEY CLUSTERED
(
[CategoryId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON
[PRIMARY]
GO
ALTER TABLE [dbo].[Category] WITH CHECK ADD CONSTRAINT [FK_Category_Category] FOREIGN KEY([ParentCategoryId])
REFERENCES [dbo].[Category] ([CategoryId])
GO
ALTER TABLE [dbo].[Category] CHECK CONSTRAINT [FK_Category_Category]
GO
对于无限层次结构,使用修改的前序树遍历算法
这是一种可能对您有用的不同方法。与 PARENT_ID 或 lft/rght 方法相比,它的维护成本略高,但检索更容易(并且更快)。
Dole 香蕉可以在产品表中。您有一个产品的 category_id。
我们需要允许一个产品有多个类别。这导致我们有一个 categories_products 连接表,其中产品可以有多个连接行。然后,我们必须决定是只在香蕉中添加 Dole 香蕉,还是在香蕉及其所有父母中添加。由于检索速度至关重要,我们将多乐香蕉放在其类别及其所有父类别中。多尔香蕉有三个类别产品连接。
使用这种结构,返回任何类别的所有项目既简单又快捷,只需一个查询。您不能在 PARENT_ID 方法中执行此操作(除非您对父母、祖父母等进行硬编码)。添加类别很容易。对产品进行分类需要在连接表中插入多行。删除和移动类别有点棘手。