基于附加列强制执行外键约束
Posted
技术标签:
【中文标题】基于附加列强制执行外键约束【英文标题】:Enforcing foreign key constraints based on additional column 【发布时间】:2019-01-09 01:56:11 【问题描述】:我有以下业务规则:
-
一个帐户可以有零个或多个许可证。
每个许可证都有一个唯一的 guid。
每个许可证都预先分配给给定帐户。
所有安装都属于给定帐户。
所有商业安装都需要许可证。
任何两个商业安装都不能使用同一个许可证。
可以删除商业安装,然后可以将许可证用于新的商业安装。
commercial_installation 只能使用已分配给安装帐户的许可证。
我如何执行最后一条规则? 商业安装只能使用已分配给安装帐户的许可证。 或者换句话说,对于给定的 guid要存储在commercial_installations
中,licenses.accounts_id
必须等于installations.accounts_id
。请参阅底部的示例数据。
CREATE TABLE IF NOT EXISTS accounts (
id INT NOT NULL AUTO_INCREMENT,
name VARCHAR(45) NOT NULL,
otherData VARCHAR(45) NULL,
PRIMARY KEY (id))
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS licenses (
guid CHAR(36) NOT NULL,
accounts_id INT NOT NULL,
otherData VARCHAR(45) NULL,
PRIMARY KEY (guid),
INDEX fk_licenses_accounts1_idx (accounts_id ASC),
CONSTRAINT fk_licenses_accounts1
FOREIGN KEY (accounts_id)
REFERENCES accounts (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS installations (
id INT NOT NULL AUTO_INCREMENT,
accounts_id INT NOT NULL,
otherData VARCHAR(45) NULL,
PRIMARY KEY (id),
INDEX fk_installations_accounts1_idx (accounts_id ASC),
CONSTRAINT fk_installations_accounts1
FOREIGN KEY (accounts_id)
REFERENCES accounts (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS open_installations (
installations_id INT NOT NULL,
otherData VARCHAR(45) NULL,
PRIMARY KEY (installations_id),
CONSTRAINT fk_open_installations_installations1
FOREIGN KEY (installations_id)
REFERENCES installations (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS commercial_installations (
installations_id INT NOT NULL,
licenses_guid CHAR(36) NOT NULL,
otherData VARCHAR(45) NULL,
PRIMARY KEY (installations_id),
UNIQUE INDEX fk_commercial_installations_licenses1_idx (licenses_guid ASC),
CONSTRAINT fk_commercial_installations_installations1
FOREIGN KEY (installations_id)
REFERENCES installations (id)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
CONSTRAINT fk_commercial_installations_licenses1
FOREIGN KEY (licenses_guid)
REFERENCES licenses (guid)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB;
给定以下示例数据:
b5060518-f87e-4acc-82c8-adb5750685a9
和d6f23460-0d77-400e-ae96-13f436e40245
只能存在于commercial_installations
中,其中commercial_installations.id
是1
或2
。
同样,8739ef62-7fff-4913-81de-3d00e8f50ecb
对应于 3
。
同样,36cc0787-5cb9-4c3a-b79d-1dcfb83d2794
对应于 4
。
帐户
+----+-----------+
| id | name |
+----+-----------+
| 1 | Account 1 |
| 2 | Account 2 |
| 3 | Account 3 |
| 4 | Account 4 |
+----+-----------+
许可证
+--------------------------------------+-------------+
| guid | accounts_id |
+--------------------------------------+-------------+
| b5060518-f87e-4acc-82c8-adb5750685a9 | 1 |
| d6f23460-0d77-400e-ae96-13f436e40245 | 1 |
| 36cc0787-5cb9-4c3a-b79d-1dcfb83d2794 | 2 |
| 8739ef62-7fff-4913-81de-3d00e8f50ecb | 3 |
+--------------------------------------+-------------+
安装
+----+-------------+
| id | accounts_id |
+----+-------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 3 |
| 4 | 2 |
+----+-------------+
【问题讨论】:
@RaymondNijland 我目前这样做,但仅限guid
。我可以创建licenses
主键组合guid-accounts_id
或添加第二个约束commercial_installations.accounts_id
引用accounts.id
。但是如果我这样做了,则不会强制执行 commercial_installations.accounts_id
与 installations.accounts_id
相同,并且我不能添加这样的约束,因为 installations.accounts_id
已经引用了 acccounts.id
。我绕了一圈又一圈……
"但是如果我这样做了,commercial_installations.accounts_id
与installations.accounts_id
相同,我无法添加这样的约束,因为 installations.accounts_id 已经引用了 acccounts.id。周围和周围我去“真的,现在我看起来更好了......我认为你需要使用 BEFORE INSERT 触发器来检查 commercial_installations.accounts_id
是否与 installations.accounts_id
相同,方法是选择许可证和帐户。你能添加一些表的示例记录?
@RaymondNijland 我添加了示例数据。我在想一个触发器。我犹豫不决,因为每当我这样做时,我都需要让它抛出一些自定义异常,无论我记录得多么好,我都会忘记它并让自己有很大的机会。如果是触发器,您建议如何实施?谢谢
在深入了解您的数据模型之后。我还想知道为什么有一个看起来不需要的commercial_installations
表。除了“商业”对我来说听起来更像是一种元数据(类型)。使用元数据作为表名或多或少是一种 SQL 反模式。我将删除 commercial_installations
表并将 installation_id
字段添加到许可证表中,也无需存储两次 guid。除非表 @987654356 @ 表示从表 installations
到坏 mysql 的继承不像 PostgreSQL 那样支持继承
@RaymondNijland - “在我看来,除了“商业”之外,它更像是一种元数据(类型)。使用元数据作为表名或多或少是一种 SQL 反模式。” hmmm ,在多个子表之间有一个具有外键弧的建模子类型的完善约定,在我看来,安装类型的实现符合这种模式。这必须比具有复杂检查约束的单个表更好,这些约束针对不同类型的安装执行规则。正如你确实承认你提到了继承。
【参考方案1】:
当然,您可以在应用程序的逻辑中强制执行它,但我想您想在数据库级别执行它。
我在这里看到的唯一选项是为INSERT
、UPDATE
和DELETE
操作添加触发器。普通外键不会为您执行此操作。
我喜欢这个问题,因为我之前也遇到过这个问题,并且没有真正花时间在 SO 中正式询问它。
【讨论】:
我目前正在应用程序中执行,但是是的,我宁愿在数据库级别执行此操作。谢谢【参考方案2】:只需将account_ID
传播到commercial_installations
。它还需要更多的唯一约束,标记为 SK(超键,键的超集),以用作模型中 FK 的目标。
【讨论】:
以上是关于基于附加列强制执行外键约束的主要内容,如果未能解决你的问题,请参考以下文章