一对多关系数据库建模:为什么大多数人选择外键而不是复合键?

问题描述 投票:0回答:1

我有一个问题,为什么大多数人选择一种有缺陷的方式在关系数据库中建模“一对多”关系。

为了简单起见,让我们考虑一个应用程序:

  • 在屏幕 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 – “拥有”实体中的复合键 在这种方法中:

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)关系可以反映两个业务需求:

  • 如果没有实体 A,实体 B 就无法存在(例如:订单/购物车的订单商品、帖子的评论等)
  • 实体 B 可以在没有实体 A 的情况下存在(例如:0..N 个用户创建/更新了单个文档,...)

我认为,在建模“没有实体 A 就无法存在实体 B”的一对多关系时,方法 2 是大多数业务用例中的最佳方法。 为什么 ?想象一下 orderitem 表包含 10 亿条记录,并且用户正在显示订单文档。 在方法1中,为了提高性能,将在非主id_order列上创建索引。所以除了10亿长的表之外,还会有另外10亿长的db实体用于b树索引。这将使数据库存储基础设施变得混乱。 在方法 1 中,应用程序服务器中的错误也可能导致 orderitem 表中的记录违反业务功能规则。

方式二中,复合主键的id_order已经作为索引,可以在log(n)时间内获取相关的实体B记录。 基础设施上无需添加额外的存储。

但是,在对“实体 B 可以在没有实体 A 的情况下存在”一对多关系进行建模时,方法 1 可能是最佳方法,但前提是大多数查询仅在实体 B 上运行。

所以我的问题是:您认为我的分析正确吗?如果是,为什么大多数数据库开发人员选择外键方法而不是复合键方法,而不考虑业务现实......?

大多数 ORM 强烈鼓励方法 1(使用外键),无论功能需求是什么/无论性能如何

sql relational-database one-to-many
1个回答
0
投票

好的,这里有很多东西要解压。

所以除了10亿长的表之外,还会有另外10亿长的数据库实体用于b树索引

是的,在 MySQL 中,需要为此目的创建和维护一个“辅助”索引。这在 InnoDB 和(SQL Server)中也是有效的,因为它们使用聚集索引模型。老实说,现在十亿条目的索引不算小,但也不算大。我在 DB2 中有一个 9 亿行的表,有 4 个索引(每个索引 2-6 列)。没什么大不了的。

应用程序服务器中的错误可能会导致 orderitem 表中的记录违反业务功能规则

不,这两种解决方案在这方面都是安全的。应用程序(任何应用程序)将无法插入不符合外键约束的数据。不用担心这个。这方面的错误将被视为关键错误,并且会快速修复。

你觉得我的分析正确吗...

是的,您的分析是正确的,尽管偏向于性能优化。我完全同意你的观点,因为我的重点也是性能优化。然而,你需要现实一点,考虑到许多开发人员并不真正理解它的基本原理(我花了几十年的时间)。再说一遍,这没有什么问题,因为在大多数情况下,引擎可以对其进行补偿(例如 Oracle 自动索引、DB2 自动调整等)

大多数 ORM 强烈鼓励方法 1 ...

我发现大多数
非成熟

ORM 确实鼓励这种方法,特别是基于 JavaScript 或 TypeScript 的方法。这些是新的 ORM,其实现并不全面,并且在使用复合键时实际上存在错误。我认为这个建议不是理论性的,而是实用性的,因为它们经常因复合键而崩溃。我确信这些错误会及时得到修复。 我希望我能够为您有趣的问题添加更多细节。

© www.soinside.com 2019 - 2024. All rights reserved.