MySQL中的分层数据

9 mysql join hierarchical-data

我的MySQL数据库中有一种类似于树的东西.

我有一个包含类别的数据库,每个类别都有一个subcat.我将所有类别保存在一个表中,因此列如下所示:

*categories table*
id | name  | parent_id
1  | Toys  | 0
2  | Dolls | 1
3  | Bikes | 1
Run Code Online (Sandbox Code Playgroud)

我的数据库中的每个项目都分配给以下类别之一:

*items table*
item   | category_id
barbie | 2
schwinn| 3
Run Code Online (Sandbox Code Playgroud)

问题是,如果有人想要查看所有TOYS(父类别),从项目数据库中获取信息的最佳方法是什么?我知道如何做的事情的唯一方法

SELECT * 
FROM items 
WHERE category_id = 2 
JOIN SELECT * 
     FROM items 
     WHERE category_id = 3
     etc... 
Run Code Online (Sandbox Code Playgroud)

但是,如果我在Toys下有10个类别,那么我必须进行10次加入和查询.

有没有更好的方法来处理这个?

Jef*_*ang 21

您希望获得父ID:

所以假设你得到了

set @parentId = 1 /*toys*/

select 
  *
from
  Items i
inner join Categories c on c.id = i.categoryId
where
  c.parentId = @parentId
Run Code Online (Sandbox Code Playgroud)

这将为您提供所需的项目 - 一个主要的设计缺陷:它不处理多层次的分层类别.

假设你有这个类别表:

*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
Run Code Online (Sandbox Code Playgroud)

和物品:

*items table*
item   | category_id
Barbie | 4
GIJoe  | 5
Schwinn| 6
Huffy  | 7
Run Code Online (Sandbox Code Playgroud)

获取所有相关项目的唯一方法是进行自我加入:

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
Run Code Online (Sandbox Code Playgroud)

此模式不可扩展 - 因为您可以拥有多层次的层次结构.

处理层次结构的一种常见方法是构建一个"扁平化"表:将每个节点链接到所有它的后代的行.

除了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
Run Code Online (Sandbox Code Playgroud)

所以你可以写:

select 
  *
from
  Items i
inner join CategoriesFlat c on c.id = i.categoryId
where
  c.parentId = @parentId
Run Code Online (Sandbox Code Playgroud)

并获取所有相关的类别和项目.

这是一个关于SQL反模式和解决方案的精彩幻灯片.(SQL中的分层数据是一种反模式,但不要沮丧 - 我们都遇到了这种情况)


Ter*_*ber 0

假设您知道玩具类别的 id,并且顶级玩具类别中没有任何内容:

SELECT * FROM items WHERE category_id IN (SELECT id FROM categories WHERE parent_id = 1)
Run Code Online (Sandbox Code Playgroud)