Django数据库设计:直接存储外键还是从关联表获取对应字段值?
结论先行
两种方案没有绝对的优劣,完全取决于你的业务场景和数据规模,下面分别拆解:
1. 查询效率对比
你自己的判断是对的:
- 直接
ProductSale.objects.filter(department_id=xxx)是单表查询,仅需要扫描product_sale表的department_id字段(如果加了索引的话速度极快),性能远高于第二种。 ProductSale.objects.filter(product__department_id=xxx)需要对product_sale和product两张表做INNER JOIN,再关联筛选字段,数据量越大、关联维度越复杂,性能差距越明显,做部门维度的全量报表时开销会尤其突出。
2. 数据一致性与业务合理性分析
你提到「销售记录对应的部门和产品所属部门完全一致」,这里要先明确一个核心业务逻辑:你需要保留销售数据的部门归属快照吗?
- 如果不需要:产品属于哪个部门,它所有历史销售记录就归到哪个部门,哪怕后续产品调整了所属部门,历史销售数据也要跟着改归属。这种情况下不冗余存字段是符合第三范式的设计,能避免数据不一致的问题,代码维护成本也更低。
- 如果需要:比如23年A产品属于部门甲,24年调整到部门乙,23年的销售数据统计时还要归到部门甲。这种场景下必须冗余存储department_id,因为通过
product.department拿到的永远是最新的部门归属,会导致历史报表完全错误。
3. 选型建议
- 数据量小于10万、查询频率低、不需要历史快照:直接用不冗余的方案即可,哪怕是连表查询性能也足够应对,不用额外做一致性维护。
- 数据量大于10万、经常做部门维度的统计报表、需要历史快照:冗余存储
department_id,创建销售记录时直接写入当前product.department_id即可,后续不需要更新,完全没有一致性风险,查询性能也最优。 - 不想冗余又想提升连表查询性能:给
product.department_id和product_sale.product_id分别加索引,能大幅降低第二种查询的开销,大部分中小业务场景足够用。
内容的提问来源于stack exchange,提问作者D_P
相关产品推荐
相关产品推荐

