树形结构在后端系统中非常常见,特别是分类管理场景,例如商品分类、组织架构等。本文将以给定的数据库表为例,探讨如何设计树结构分类数据模型及其优点。
一、数据表设计
1. 分类节点表
CREATE TABLE `product_classification_node` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键(分类ID)',
`classification_name` varchar(50) NOT NULL DEFAULT '' COMMENT '分类名称',
`parent_id` bigint(20) NOT NULL DEFAULT '-1' COMMENT '分类父ID',
`classification_desc` varchar(511) NOT NULL DEFAULT '' COMMENT '分类描述',
`is_enabled` tinyint(1) NOT NULL DEFAULT '-1' COMMENT '是否启用 | 0:未启用 1:启用',
`create_user_id` varchar(50) NOT NULL DEFAULT '' COMMENT '创建用户id',
`create_user_name` varchar(20) NOT NULL DEFAULT '' COMMENT '创建用户姓名',
`update_user_id` varchar(50) NOT NULL DEFAULT '' COMMENT '更新用户id',
`update_user_name` varchar(20) NOT NULL DEFAULT '' COMMENT '更新用户姓名',
`time_update` bigint(20) NOT NULL COMMENT '更新时间',
`time_create` bigint(20) NOT NULL COMMENT '创建时间',
`product_count` int(11) NOT NULL DEFAULT '0' COMMENT '商品数量',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_name_pid` (`parent_id`,`classification_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分类节点表';
该表主要记录分类的基本信息和层级关系,通过parent_id字段表示父子关系。
2. 闭包表
CREATE TABLE `product_classification_node_closure` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`descendant_id` bigint(20) NOT NULL COMMENT '后代节点 ID',
`ancestor_id` bigint(20) NOT NULL COMMENT '祖先节点 ID',
`depth` tinyint(3) NOT NULL COMMENT '祖先节点到后代节点的层数差',
`time_update` bigint(20) NOT NULL COMMENT '更新时间',
`time_create` bigint(20) NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_ancestorid_descendantid` (`ancestor_id`,`descendant_id`),
KEY `k_descendantid` (`descendant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分类节点闭包表';
闭包表记录了每个节点与其所有祖先和后代的关系,depth字段表示层级差。
3. 分类商品关联表
CREATE TABLE `classification_product` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`productclassificationnode_id` bigint(20) NOT NULL DEFAULT '-1' COMMENT '分类表主键ID',
`product_id` bigint(20) NOT NULL DEFAULT '-1' COMMENT 'product 表主键ID',
`product_code` int(11) NOT NULL DEFAULT '-1' COMMENT '商品编码',
`create_user_id` varchar(50) NOT NULL DEFAULT '' COMMENT '创建用户id',
`create_user_name` varchar(20) NOT NULL DEFAULT '' COMMENT '创建用户姓名',
`update_user_id` varchar(50) NOT NULL DEFAULT '' COMMENT '更新用户id',
`update_user_name` varchar(20) NOT NULL DEFAULT '' COMMENT '更新用户姓名',
`time_update` bigint(20) NOT NULL COMMENT '更新时间',
`time_create` bigint(20) NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_procutid_nodeid` (`product_id`,`productclassificationnode_id`),
KEY `k_productclassificationnodeid_areazip` (`productclassificationnode_id`,`area_zip`),
KEY `k_productcode_areazip` (`product_code`,`area_zip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='分类商品表';
此表实现商品与分类节点的关联,支持快速查询商品所属分类。
二、设计原则与实现
- 清晰的层级关系:
- 在
product_classification_node中,通过parent_id实现父子节点关系。数据插入时即可构建树状结构。 - 使用
product_classification_node_closure闭包表,便于快速查询某一节点的所有祖先或后代,优化层级遍历性能。
- 在
- 性能优化:
- 闭包表的优势:
- 单表无法高效支持深层次树结构查询(例如查询所有祖先节点)。
- 闭包表提前存储了节点间的路径信息,使查询复杂度由递归降为单表查询。
- 索引设计:
- 主键索引与
UNIQUE KEY保证数据唯一性,防止重复插入。 - 针对高频查询字段设置索引,例如
ancestor_id、descendant_id,支持多级分类的高效查询。
- 主键索引与
- 闭包表的优势:
- 灵活的商品关联:
classification_product表支持商品与分类多对多关系管理,灵活应对商品多分类场景。- 索引
uk_procutid_nodeid和k_productclassificationnodeid_areazip便于快速定位商品分类和地域维度。
三、常见功能实现
-
获取分类树:
SELECT * FROM product_classification_node WHERE is_enabled = 1;- 根据
parent_id递归构建树。
- 根据
-
查询某分类的所有后代:
SELECT descendant_id FROM product_classification_node_closure WHERE ancestor_id = ?; -
查询某分类的所有祖先:
SELECT ancestor_id FROM product_classification_node_closure WHERE descendant_id = ?; -
统计分类下商品数量:
SELECT SUM(product_count) FROM product_classification_node_closure c JOIN product_classification_node n ON c.descendant_id = n.id WHERE c.ancestor_id = ?;
四、设计优点
- 高效的层级查询:
- 闭包表避免了递归查询的性能问题。
- 适用于需要频繁查询祖先、后代节点的场景。
- 灵活扩展性:
- 支持复杂分类树形结构的动态调整,例如新增、删除、移动节点。
- 商品与分类的多对多设计满足不同业务需求。
- 清晰的结构:
- 分类节点、闭包表、商品关联分表设计,分工明确,便于维护和扩展。
- 稳定性:
- 完整的索引设计与唯一约束减少数据一致性问题。
五、适用场景
- 商品分类管理系统
- 组织架构管理
- 内容管理系统(CMS)中的栏目分类
- 权限管理中的资源树
通过这样的设计,不仅能够满足树结构数据模型的基本需求,还具备良好的性能和扩展性,适合于复杂的后端分类管理场景。
1042

被折叠的 条评论
为什么被折叠?



