内容提要
数据库设计中过度规范化会导致问题。以产品和销售为例,若销售表外键设为级联更新删除,删除产品会破坏历史销售数据,且售价与标价无关。销售数据应独立存储,保留交易时的历史信息。
延伸解读
过度规范化的典型陷阱
文章指出,新手常犯的错误是过度规范化,尤其是依赖AI生成数据模型时。例如,将销售表的产品外键设置为级联更新和删除,会导致删除产品时自动清除历史销售记录,破坏数据完整性。这种设计错误不仅限于PostgreSQL,所有支持外键的关系数据库都可能遇到。
销售数据应独立存储
销售价格可能与产品标价无关,因此销售数据不应直接引用产品表。文章建议将销售视为独立实体,存储交易时的历史信息,如实际售价、时间等,而不设置外键约束。这样能确保历史数据按原样保留,避免因产品信息变更或删除而丢失或出错。
数据类型选择与业务复杂性
文章强调使用numeric而非浮点类型存储价格,以避免计算误差。同时提醒,真实业务中价格、产品、销售涉及税费、折扣、欺诈等众多因素,数据模型可能包含数十张表。设计时需密切关注历史数据、变更和报表需求,避免基本错误。
Q&A
在PostgreSQL中设计产品和销售数据模型时,常见的过度规范化错误是什么?
常见的错误是过度规范化,例如在销售表中将产品外键设置为ON UPDATE CASCADE ON DELETE CASCADE。这会导致删除产品时自动删除历史销售数据,破坏需要保留的交易记录。
为什么在销售表中使用ON DELETE CASCADE外键约束是不好的?
因为销售数据是历史记录,必须保留。如果使用ON DELETE CASCADE,删除产品时会自动删除所有相关的销售记录,导致历史数据丢失,影响报告和分析。
销售价格和产品标价有什么关系?为什么不能直接使用产品表中的价格?
销售价格可能与产品标价完全不同,因为实际销售中可能有折扣、税费、议价等。因此销售数据应独立存储交易时的实际价格,而不是引用产品表的当前价格。
如何修正产品和销售数据模型中的过度规范化问题?
应该重新设计销售表,移除对产品表的外键约束(特别是级联更新和删除),将销售数据作为独立实体存储,保留交易时的历史信息,如产品ID、销售价格和时间戳。
在PostgreSQL中存储价格时应该使用什么数据类型?为什么?
应该使用numeric类型,而不是float4或float8,以避免浮点数计算错误,确保价格计算的精确性。
过度规范化问题只存在于PostgreSQL中吗?
不是,这些问题在所有提供外键和引用完整性实现的关系型数据库中都会出现,不仅限于PostgreSQL。