【问题标题】:Advice on Hierarchical MySQL Database Design关于分层 MySQL 数据库设计的建议
【发布时间】:2026-02-01 11:05:01
【问题描述】:

我是数据库设计的新手,我只是想就我是否以合乎逻辑的方式处理这个问题提出一些意见。我正在构建一个简单的 MySQL 数据库,用户将通过该数据库将项目上传到以前存在的(不变的)分层树。

举个简单的例子:

第 1 节

   1.1 部分
细分 1.1.1
细分 1.1.2

   第 1.2 部分
细分 1.2.1
细分 1.2.2

第 2 节

   第 2.1 节
细分 2.1.1
细分 2.1.2

   第 2.2 节
细分 2.2.1
细分 2.2.2

树的结构不会改变,用户只需上传属于细分的产品(一种行业特定的方式来组织大量产品)。我已经对邻接列表和嵌套集进行了研究,但我倾向于使用 3 个单独的表,每个表都引用其父主键(因为树的顶层几乎永远不会改变)。上传新产品时,它将引用其所有三个父级(如果它在 1.1.2 小节下归档,则它必然是第 1 节第 1 节的一部分)。最终的树将有 4 个部分,每个部分有 10 个分区,每个分区有 10 个细分。这作为一个起始策略有意义吗?

与数据库的交互或多或少仅限于输入信息并对其进行准确分类,然后能够显示在任一部分、部门或细分中已归档的产品数量。该库将显示在一系列下拉列表中,单击列表项将显示存储的信息。

任何推荐或参考文献/教程将不胜感激!

【问题讨论】:

  • 如果您永远不会拥有超过 3 个级别,那么您可以“便宜”并简单地拥有 (section_id, div_id, subdiv_id) 并将其全部保存在一个表中。但是,它很容易受到像sec #1, div 2.1, subsec 1.1.1 这样的层次结构错误的影响。
  • 我同意 Marc B 的观点,您的结构在技术上是分层的,但它的结构不够动态,不足以保证高级数据结构。您可以通过简单的约束避免类别错误
  • 非常感谢马克和大卫!大卫,你能扩展一下简单约束的含义吗? Marc B,如果所有产品都需要分配到一个小节(例如“Bill's Macintosh Apple Orchard”在 AmericanFruit>Apples>Macintosh 下归档),那么我是否正确地说我会有一张描述不变树的表,并且然后是第二个表,其中包含“Bill's Macintosh Apple Orchard”的特定信息和 id 以及对其父 subdiv_id 的引用?我希望这很清楚:)

标签: php mysql database hierarchy


【解决方案1】:

因为“树的结构不会改变”,您需要sectiondivisionsubdivision 表(还有产品)。

create table section (
  id int primary key,
  name varchar(100) 
);

create table division (
  id int primary key,
  name varchar(100) ,
  section_id int references section
);

create table subdivision (
  id int primary key,
  name varchar(100) ,
  division_id int references division
);

create table product (
  id int primary key,
  name varchar(100) ,
  subdivision_id int references subdivision
);

对于其他requerimets,例如:

  • 未知深度树。
  • 将产品分配到多个树级别。

你会寻找一个亲子解决方案,例如:

create table tree (
  id int primary key,
  parent_id int null references tree,
  name varchar(100) 
);

create table product (
  id int primary key,
  name varchar(100) ,
  subdivision_id int references tree
);

【讨论】:

  • 非常感谢!这正是我想要表达的。我开始了这个过程,但不确定它是否被视为可接受的解决方案。
【解决方案2】:

由于类别(部分)或多或少是静态的,您可以为每个类别分配一个“高”和“低”数字,例如

catid  name        low      high
1      Section1     1       20
2      Div1.1       2       10
3      Div1.2       11      19
4      Section2     21      40
5      Div2.1       22      29
6      Div2.2       30      39

然后有一个单独的表格来存放内容:

id   catid   content
1    2       fileA 
2    2       fileB
3    5       fileC

然后要查询第 1 部分下的所有项目,您只需查询一个类别中的所有项目,其高低在 1 到 20 之间。要获取 Div2.1(及以下)中的所有项目,您查询所有项目类别的高低在 22 到 29 之间。这使得跟踪子类别下的项目数量变得非常容易。

我忘记了这种实际方法的名称(如果有确定的方法),但我已经用过很多次了。对于变化不大的结构,它比传统的 parent_id-child_id 类型结构更容易使用。

【讨论】:

  • GrandmasterB 和其他人,感谢您的意见。实际上,我之前已经在嵌套集合图中绘制了模式(花了很长时间,大约有 400 个最终细分)。我的问题从您描述的第二张表开始。项目必须在细分下进行分类,因此所有输入的信息将始终处于同一级别。将 fileC 输入到 Section 2, Division 2, Subdivision 1 的代码 sn-p 是什么样的(来自您的示例)?另外(在黑暗中拍摄)是否有任何简单的方法可以将低/高数字分配给大量树节点?
  • 您可以将递归表转换为嵌套集。检查这个帖子:sqlblog.com/blogs/adam_machanic/archive/2006/07/12/… 还有这个:jsimonbi.wordpress.com/2011/02/14/nested-set-hierarchy
  • @TheNally,添加一个“底部”列来表示树底部的类别。然后在添加文件时,只允许将文件添加到那些类别中。
【解决方案3】:

一个好的解决方案是有一个递归表。

在 * 上查看此帖子:Hierarchical Data in MySQL

这样,您的设计将支持在树下设置更多级别。

关于此主题的其他有趣文章:

Managing Hierarchical Data in MySQL

Hierarchical data in MySQL: parents and children in one query

Hierarchical data in MySQL: easy and fast

Recursion-less storage of hierarchical data in a relational database

【讨论】:

  • 感谢您的参考,其中一些我已经检查过了,但我一定会进一步研究递归表。再次感谢!
【解决方案4】: