Tri*_*Nhu 5 database django postgresql database-design
我必须实现以下层次结构数据:
Category (id, name, url)
SubCategory (id, name, url)
SubSubCategory (id, name, url)
Run Code Online (Sandbox Code Playgroud)
注意,这是多对多关系。EG:每个节点可以有多个父级或子级。没有流通关系(感谢上帝)。只有某些SubSubCategory可以属于多个SubCategory。
我的实现:为此,我使用单个表
Cat (id, type(category, subcategory, subsubcategory), name, url)
CatRelation (id, parent_id, child_id, pre_calculated_index for tree retrieval)
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
检索树时,只需联接所有表。他的论据是,稍后将某些属性添加到不需要的任何类别类型时,在单表实现中将字段设为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 |
Run Code Online (Sandbox Code Playgroud)
因此,当您按路径排序(或在修改后的预排序树中按左排序)时,您将获得以下不错的树结构而无需递归:
| 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. |
Run Code Online (Sandbox Code Playgroud)
而且我总是可以使用递归动态构建路径:
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;
Run Code Online (Sandbox Code Playgroud)
这不是有人建议的继承
您的问题有些固执己见,因为您要求比较两种不同的方法。我会尝试提供一个答案,尽管恐怕没有唯一的真实答案。在答案的其余部分中,我将把您的方法称为解决方案 A,将您老板建议的方法称为解决方案 B。
我强烈建议遵循你老板提出的方法:
特别适用 Python 之禅的以下规则:
你可能会以美元来衡量绩效ms,但你的老板最终会以美元来衡量绩效。让初级开发人员加入解决方案 B 所需的时间要少得多。时间对于企业来说是昂贵的。
模型的未来变化可以更容易地实现。如果您想添加另一个Category不应该(或不需要)出现在SubCategoryand中的字段,该怎么办SubSubCategory?
使用解决方案 B 测试(单元和功能)要容易得多。它最终需要更多行代码并且更冗长,但更容易阅读和理解。
性能会有所不同,具体取决于用例。数据库中有多少条记录?更关键的是:检索还是插入/更新?使早期性能更高的因素可能会降低后者的性能,反之亦然。
我希望你听过这句话:
过早的优化是万恶之源。
由唐纳德·高德纳 (Donald Knuth) 提供。
当出现具体问题时,您会关心性能。这并不意味着您在设计应用程序时不应该对性能进行任何深思熟虑。
您可以缓存查询,一个选项是使用redis. 既然你使用了,PostgreSQL你也可以使用物化视图。但正如我所说,当我到达那座桥时,我会跨过那座桥。
编辑:您没有提到任何其他模型。我假设当你有类别时,你会有一些实体,比如说分类在这些类别中的产品,即分类的产品。这里我举个例子:
如果您严格遵循此层次结构并仅将产品放入 SubSubCategory 中,那么解决方案 B 更好。
但是,如果您有一个虚构的产品Sportskit ACME(跑鞋、短裤和无袖衬衫),您无法将其放入 SubSubCategory 中,并且需要跳过一级将其放入 SubCategory 中,那么您最终可能会使用类似genericrelations的东西。
在这种情况下,解决方案 A 更好。
| 归档时间: |
|
| 查看次数: |
170 次 |
| 最近记录: |