我必须实现以下层次结构数据:
Category (id, name, url)
SubCategory (id, name, url)
SubSubCategory (id, name, url)
请注意,这是多对多关系。 EG:每个节点可以有多个父级或子级。没有流通关系(感谢上帝)。只有某些SubSubCategory可以属于多个SubCategory。
我的实现:为此,我使用单个表
Cat (id, type(category, subcategory, subsubcategory), name, url)
CatRelation (id, parent_id, child_id, pre_calculated_index for tree retrieval)
pre_calculated_index
可以是修改后的预排序树遍历的 [1,2] 或实现中的路径的左右实现。 pre_calculated_index
的计算是在将子级添加到一个节点时进行的,因此,当您检索一棵树时,只需要按此字段排序即可,避免进行递归查询。
无论如何,我的老板认为这种实现方式并不理想。他建议为每种类型的类别都有一个表,然后再有一个将它们链接起来的数据透视表:
Category (id, name, url)
SubCategory (id, name, url)
SubSubCategory (id, name, url)
Category_SubCategory(category_id, sub_category_id)
SubCategory_SubSubCategory(sub_category_id, sub_sub_category_id)
检索树时,只需联接所有表。他的论据是,稍后将某些属性添加到不需要的任何类别类型时,在单表实现中将字段设为null。由于pre_calculated_index
是用代码计算的,因此可能会出错。
我应该跟随哪个?哪个性能更好?
我使用django和postgreSQL。
PS:有关我的pre_calculated_index
实现的更多详细信息:
我为CatRelation添加一个路径(字符串,唯一,索引)值,而不是为每个节点左右移动:根节点将具有“ path ='”。
子节点添加到CatRelation后,将具有path = parent_path +'。因此,当您按此路径排序时,将按树顺序获得所有内容。例子:
Cat
| id | name | url |
|----|------------|-----|
| 1 | Cat1 | |
| 2 | Subcat1 | |
| 3 | Subcat2 | |
| 4 | Subcat3 | |
| 5 | Subsubcat1 | |
| 6 | Subsubcat2 | |
| 7 | Subsubcat3 | |
CatRelationship Left right equivalent
| id | parent_id | child_id | path | |lft |rght|
|---- |----------- |---------- |-------- | |----|----|
| 1 | null | 1 | 1. | | 1 | 14 |
| 2 | 1 | 2 | 1.2. | | 2 | 3 |
| 3 | 1 | 3 | 1.3. | | 4 | 11 |
| 4 | 1 | 4 | 1.4. | | 12 | 13 |
| 5 | 3 | 5 | 1.3.5. | | 5 | 6 |
| 6 | 3 | 6 | 1.3.6. | | 7 | 8 |
| 7 | 3 | 7 | 1.3.7. | | 9 | 10 |
因此,当您按路径排序(或在修改后的预排序树中按左顺序排序)时,您将获得这种不错的树结构,而无需递归:
| id | parent_id | child_id | path |
|---- |----------- |---------- |-------- |
| 1 | null | 1 | 1. |
| 2 | 1 | 2 | 1.2. |
| 3 | 1 | 3 | 1.3. |
| 5 | 3 | 5 | 1.3.5. |
| 6 | 3 | 6 | 1.3.6. |
| 7 | 3 | 7 | 1.3.7. |
| 4 | 1 | 4 | 1.4. |
而且我总是可以使用递归动态构建路径:
WITH RECURSIVE CTE AS (
SELECT R1.*, CONCAT(R1.id, ".") AS dynamic_path
FROM CatRelation AS R1
WHERE R1.child_id = request_id
UNION ALL
SELECT R2.*, CONCAT(dynamic_path, R2.child_id, ".") AS dynamic_path
FROM CTE
INNER JOIN CatRelation AS R2 ON (CTE.child_id = R2.parent_id)
)
SELECT * FROM CTE;