【问题标题】:MySQL create both entities with cyclic foreign keyMySQL使用循环外键创建两个实体
【发布时间】:2022-01-20 19:03:50
【问题描述】:

我想要一个“实体”和它的许多版本,其中一个版本是唯一一个处于活动/使用状态的版本。实体也有可能完全处于非活动状态。所以我想到了使用两个带有循环外键的表,如下所示:

CREATE TABLE entity (
  id int NOT NULL AUTO_INCREMENT,
  -- some extra irrelevant data commented out
  active_version_id int DEFAULT NULL,
  PRIMARY KEY (id)
);
CREATE TABLE entityversion (
  id int NOT NULL AUTO_INCREMENT,
  -- some extra irrelevant data commented out
  entity_id int NOT NULL,
  PRIMARY KEY (id)
);
ALTER TABLE entity ADD FOREIGN KEY (active_version_id) REFERENCES entityversion(id) ON DELETE SET NULL;
ALTER TABLE entityversion ADD FOREIGN KEY (entity_id) REFERENCES entity(id) ON DELETE CASCADE;

我想在创建一个新的活动实体时,同时创建它的第一个实体版本,这将是它的活动版本。问题是我们还没有他们的身份证。目前,我们正在使用“返回 id”创建实体,并使用它来创建 EntityVersion,也使用“返回 id”,然后更新同一实体的 active_version_id,因此例如 3 个单独的命令:

INSERT INTO entity DEFAULT VALUES RETURNING id;
-- get the ID back and use it as a parameter to the next command
INSERT INTO entityversion (entity_id) VALUES (%s) RETURNING id;
-- again the same thing
UPDATE entity SET active_version_id = %s WHERE id = %s;

我想知道是否有更短的方法可以做到这一点。如果碰巧是更好的选择,我也接受表模式的不同方法作为答案。感谢您的帮助!

【问题讨论】:

  • re ON DELETE SET NULL,您会删除处于活动状态的实体版本的情况是什么?
  • 您的外键将不起作用,因为您需要彼此在同一本书中。从实体中删除一个并制作一个触发器
  • CREATE TABLE entity 将因entityversion 不存在而失败。
  • 我想在创建一个新实体时,同时创建它的第一个 EntityVersion,这将是它的 active_version。 形式上 - 你可以使用相应的触发器。真的 - 这个自动创建的行的“my data here”必须从哪里获取?
  • @ysth 如果由于某种原因删除了活动版本,我不希望实体被销毁,就是这样。

标签: mysql database-design


【解决方案1】:

在存储过程中创建您的两行,或者如果实体版本版本表中没有仅的数据,则使用插入前触发器。为了处理您的循环 id 问题,在 mariadb 中使用序列而不是 auto_increment。在 mysql 中,使用仅包含 auto_increment id 的 entity_sequence 表来模拟序列。在您的存储过程/触发器中,获取序列值(如果模拟序列,则使用 insert..returning id),使用该值存储 entityversion,然后将 entityversion id 设置为存储在您的实体行中。

【讨论】:

  • 嘿嘿。如果您认为这合适,请告诉我:而不是序列表,而是先从 entityversion 表中获取下一个 auto_increment id(在触发器或事务中,我认为这将是一个事务,因为我只想在我真的想同时创建实体和版本)并将其用作第一个 INSERT into entity 的参数,从而无需执行该 UPDATE 命令。你会推荐这个吗?还是有理由使用序列表?
  • 您必须先创建实体版本才能选择;没有一种安全的方法可以在不实际创建行的情况下读取下一个自动增量 ID 将是什么,并创建您需要实体 ID 的行。
  • 抱歉,我想得不够仔细;由于外键约束,您不能先创建任一行;创建两者后,您必须使用活动实体版本更新实体。所以序列或模拟序列根本没有帮助。
【解决方案2】:

您是在暗示实体是 1:1,在这种情况下,它们也可能在同一个表中。 (如果稍后才插入,则将其设为 NULLable 之一。)

如果它是 1:many(一个“最新”版本和许多“旧”版本),那么 FK 只会采用一种方式。

在任何一种情况下,您的“循环”FK 都会消失。

但要回答你的问题:

  1. 关闭 FK 检查
  2. 创建两个表
  3. 填充两个表
  4. ALTER 添加两个 FK
  5. 开启 FK 检查。

更多

嗯,看起来你有很多:1,而不是 1:1。 “历史”有一列是“当前”(“活动”)表中的“id”。没有循环 FK。索引该列,以便您可以有效地采用另一种方式。 ON DELETE CASCADE 在任何一个方向上都不实用。

FK 应该去一个方向,而不是两个方向。

【讨论】:

  • 嗨瑞克,感谢您的回复。是的,我的用例正是那个单一实体的多版本(但不完全是最新/最旧的,它与时间无关)。 FK 检查不是问题,因为 active_version_id 是一个可为空的 FK,所以它不会阻止任何东西(在我的用例中,我确实可以拥有一个没有活动版本的实体)。我刚刚在我的问题中添加了更多细节,对于不够清晰感到抱歉
  • @JordiReinsma - 我添加了更多。
猜你喜欢
  • 2020-11-04
  • 1970-01-01
  • 2018-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多