一对多关系数据库建模:为何多数人选外键而非复合键?
一对多关系建模:为何多数开发者选择“有缺陷”的方案?
我有一个疑问:为何多数人在关系型数据库中建模“一对多”关系时,会选择一种看似有缺陷的方式?
为简化说明,我们以一款事务型应用为例:
- 界面1:显示订单列表
- 界面2:显示单个订单的0至N个订单项
这是众多处理文档与工作流的事务型应用的典型基础场景。数据库建模至少有两种主要方案,以下以MariaDB为例进行说明。
方案1:仅通过外键关联的独立数据库实体
该方案下:
CREATE TABLE `order` ( id_order INT, orderfield1 VARCHAR(255), PRIMARY KEY (id_order) );
表结构如下:
+-------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+-------+ | id_order | int(11) | NO | PRI | NULL | | | orderfield1 | varchar(255) | YES | | NULL | | +-------------+--------------+------+-----+---------+-------+
以及订单项表:
CREATE TABLE `orderitem` ( id_order INT, id_item INT, orderitemfield1 VARCHAR(255), PRIMARY KEY (id_item), FOREIGN KEY (`id_order`) REFERENCES `order`(`id_order`) );
表结构如下:
+-----------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------------+--------------+------+-----+---------+-------+ | id_order | int(11) | YES | MUL | NULL | | | id_item | int(11) | NO | PRI | NULL | | | orderitemfield1 | varchar(255) | YES | | NULL | | +-----------------+--------------+------+-----+---------+-------+
方案2:“从属”实体采用复合主键
该方案下,订单表与方案1一致:
CREATE TABLE `order` ( id_order INT, orderfield1 VARCHAR(255), PRIMARY KEY (id_order) );
表结构如下:
+-------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------------+--------------+------+-----+---------+-------+ | id_order | int(11) | NO | PRI | NULL | | | orderfield1 | varchar(255) | YES | | NULL | | +-------------+--------------+------+-----+---------+-------+
订单项表采用复合主键:
CREATE TABLE `orderitem` ( id_order INT, id_item INT, orderitemfield1 VARCHAR(255), PRIMARY KEY (id_order, id_item), FOREIGN KEY (`id_order`) REFERENCES `order`(`id_order`) );
表结构如下:
+-----------------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------------+--------------+------+-----+---------+-------+ | id_order | int(11) | NO | PRI | NULL | | | id_item | int(11) | NO | PRI | NULL | | | orderitemfield1 | varchar(255) | YES | | NULL | | +-----------------+--------------+------+-----+---------+-------+
我的分析与观点
实际业务场景是选择方案1或方案2的唯一依据。我认为“一对多”(实体A-实体B)关系可对应两种业务需求:
- 实体B无法脱离实体A存在(例如:订单的订单项/购物车、帖子的评论等)
- 实体B可脱离实体A存在(例如:创建/更新单个文档的0至N个用户等)
在建模“实体B无法脱离实体A存在”的一对多关系时,方案2是多数业务场景下的最优选择,原因如下:
假设orderitem表包含10亿条记录,用户要查看某一订单文档。方案1中,为提升性能需在非主键的id_order列创建索引,除10亿行的表外,还会新增一个10亿规模的B树索引实体,占用额外存储资源。此外,应用服务器的错误可能导致orderitem表中出现违反业务规则的记录。
而方案2中,复合主键的id_order本身即可作为索引,以log(n)时间获取相关实体B记录,无需额外占用存储资源。
不过,在建模“实体B可脱离实体A存在”的一对多关系时,若多数查询仅针对实体B,方案1可能是最优选择。
请问我的上述分析是否正确?若正确,为何多数数据库开发者会脱离业务实际,选择外键方案而非复合键方案?多数ORM也会大力推崇外键方案,无论功能需求与性能考量。
内容的提问来源于stack exchange,提问作者ChristopheN
相关产品推荐
相关产品推荐

