我的MySQL数据库中有一种类似于树的东西。
我有一个包含类别的数据库,每个类别都有一个subcat。我将所有类别保存在一个表中,因此列是这样的:
*categories table*
id | name | parent_id
1 | Toys | 0
2 | Dolls | 1
3 | Bikes | 1
我的数据库中的每个项目都分配到以下类别之一:
*items table*
item | category_id
barbie | 2
schwinn| 3
问题是如果有人想要查看所有TOYS(父类别),从items数据库中获取信息的最佳方法是什么?我知道怎样做的唯一方法就是
SELECT *
FROM items
WHERE category_id = 2
JOIN SELECT *
FROM items
WHERE category_id = 3
etc...
但是,如果我在Toys下有10个类别,那么我必须进行10次加入和查询。
有没有更好的方法来解决这个问题?
答案 0 :(得分:21)
您希望获得父ID:
所以假设你得到了
set @parentId = 1 /*toys*/
select
*
from
Items i
inner join Categories c on c.id = i.categoryId
where
c.parentId = @parentId
这将为您提供您想要的项目 - 有一个主要的设计缺陷:它不能处理多层次的分层类别。
假设你有这个类别表:
*Categories table*
id | name | parentId
1 | Toys | 0
2 | Dolls | 1
3 | Bikes | 1
4 | Models | 2
5 | Act.Fig.| 2
6 | Mountain| 3
7 | BMX | 3
和物品:
*items table*
item | category_id
Barbie | 4
GIJoe | 5
Schwinn| 6
Huffy | 7
获取所有相关项目的唯一方法是进行自我加入:
select
*
from
Items i
inner join Categories c on c.id = i.categoryId
inner join Categories c2 on c.parentId = c2.id
where
c2.parentId = @parentId
此模式不可扩展 - 因为您可以拥有多层次的层次结构。
处理层次结构的一种常用方法是构建一个“扁平化”表:将每个节点链接到所有它的后代的行。
除了Categories表,您还可以构建第二个表:
*CategoriesFlat table* The Name column is here only for readability
id | name | parentId
1 | Toys | 1
-----------------
2 | Dolls | 1
2 | Dolls | 2
-----------------
4 | Models | 1
4 | Models | 2
4 | Models | 4
5 | Act.Fig.| 1
5 | Act.Fig.| 2
5 | Act.Fig.| 5
-----------------
3 | Bikes | 1
3 | Bikes | 3
-----------------
6 | Mountain| 1
6 | Mountain| 3
6 | Mountain| 6
7 | BMX | 1
7 | BMX | 3
7 | BMX | 7
所以你可以写:
select
*
from
Items i
inner join CategoriesFlat c on c.id = i.categoryId
where
c.parentId = @parentId
获取所有相关的类别和项目。
这是一个great slideshow about SQL anti-patterns及其解决方案。 (SQL中的分层数据是一种反模式,但不要沮丧 - 我们都遇到了这种情况)
答案 1 :(得分:4)
但如果我在Toys下有10个类别,那么我必须进行10次加入和查询。有没有更好的方法来解决这个问题?
是的,有一种方法可以存储名为“nested sets”的数据。插入数据有点困难,但使用单个select
语句选择整个多级分支很简单。
此外,Celko撰写了关于此主题的a book,其中有关于嵌套集的章节以及有关其他方法的其他章节。
答案 2 :(得分:3)
我假设您知道如何获取身份证号码,这不是问题的关键。此外,parent_id
也应该是引用id
的FK,我会使用NULL作为最顶层,而不是0.
如果您的最顶级类别最多只有一个级别的子类别,您可以使用此查询来获取所有玩具:
SELECT *
FROM items
WHERE items.category_id IN (SELECT id FROM categories
WHERE categories.parent_id = 1
OR categories.id = 1);
如果您的类别可以具有嵌套的子类别,则必须使用存储过程并以递归方式调用它。伪代码:
Procedure getItemsInCategory
Input: @category_id integer
Output: items rows
{
For each item in (SELECT *
FROM items
WHERE items.category_id = @category_id):
return the row;
For each id in (SELECT id
FROM categories
WHERE categories.parent_id = @category_id):
return the rows in getItemsInCategory(id);
}
答案 3 :(得分:0)
假设您知道Toys类别的ID,并且顶级Toys类别中没有任何内容:
SELECT * FROM items WHERE category_id IN (SELECT id FROM categories WHERE parent_id = 1)
答案 4 :(得分:0)
答案 5 :(得分:0)
我对MySQL并不熟悉,但这是我在TSQL(SQL SERVER)中的方式,也许试着在MySQL中找到一种相同的方法吗?
1)遍历所有类别以获取特定项目的子项,在本例中为categorie id = 1
2)在Hierarchy CTE(公用表表达式)中将项目过滤到与子项相关的项目。
With Hierarchy As
(
SELECT id, name, parent_id
from categories
where id = 1
UNION ALL
SELECT child.id, child.name, child.parent_id
from categories child
inner join Hierarchy parent on child.parent_id = parent.id
)
SELECT * FROM items
WHERE category_id IN
(
Select id
from Hierarchy
)
答案 6 :(得分:0)
此主题可能有所帮助:http://forums.mysql.com/read.php?10,32818,32818#msg-32818
你真正想要的是START WITH和CONNECT BY语法,但这仅在Oracle中支持,而不是MySQL。
答案 7 :(得分:0)
我想和你分享我的想法。
邻接模型的限制: 关注Managing Hierarchical Data in MySQL 正如您已经描述的那样,邻接模型具有限制,您必须在检索路径之前知道级别。
使用嵌套模型: 但是,如果将数据结构转换为嵌套集模型,那么您仍然可以使用自联接来获取树。
将分层模型转换为嵌套模型: 现在我们需要一个树形旅行算法来索引嵌套模型。这可以在mysql函数中实现(对不起,转换需要一些算法实现:树遍历算法。不确定哪一个最合适)。
谢谢:)