两个外键,其中一个不是 NULL:SQL 如何解决这个问题?

Two foreign keys, one of them not NULL: How to solve this in SQL?

我有一个 table time。时间条目 (1:n 关系 ) 要么属于 project 条目,要么属于 special_work 条目。必须设置项目 id 或 special_work id,两者都不能设置(exclusive 或 )。

CREATE TABLE `time` (
  `id` int(20) NOT NULL AUTO_INCREMENT,
  `project` int(20) NOT NULL,
  `special_work` int(20) NOT NULL,
  `date` date NOT NULL,
  `hours` float NOT NULL,
  `time_from` time DEFAULT NULL,
  `time_to` time DEFAULT NULL,
  `notes` text NOT NULL,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`project`) REFERENCES `project`(`id`)
  FOREIGN KEY (`special_work`) REFERENCES `special_work`(`id`)
) DEFAULT CHARSET=utf8;

如何在 SQL 中写这个?除了 触发器 之外还有什么办法吗?

如果您确定这是糟糕的数据库设计 - 是否有更好的建模方法?但是我想要两个不同的时间tables

我的数据库是 Mysql 5.5 InnoDB。

您的数据模型没问题。在大多数数据库中,您还会添加一个 check 约束:

alter table `time` add constraint chk_time_project_special_work
    check (project is not null xor special_work is null);

但是,MySQL不支持检查约束。如果你真的喜欢,你可以使用触发器来实现逻辑。

像这样的互斥引用可能表明您的设计中缺少 "supertype" table。考虑创建一个代表所有 "work" 的新超类型 table - 包括项目和特殊工作。项目和特殊工作 tables 将引用超类型 table,一对一,它将包含所有 project/work 标识符的联合。然后 Time table 只需要一个引用超类型的不可空外键。

;在 MySql 中,触发器是实现此约束的最佳方式。您有不想使用触发器的具体原因吗?

由于您明确要求除触发器之外的选项,这里有一些替代选项:

存储过程

使用存储过程在此 table 中执行插入操作/在此处包含验证逻辑。

这确保通过存储过程插入/更新的任何内容都受到保护;但有一个问题,即有人直接将数据插入 table.

可能会绕过此验证

可以通过使用安全性来缓解该问题,以确保只有存储过程有权插入此 table/更新这些字段。有关设置此安全性的更多详细信息,请参阅此答案:

客户代码

类似于存储过程选项;如果您拥有将执行这些 inserts/updates 的所有代码,则可以在数据库外部添加验证。同样,您的验证可以被绕过(即直接进入数据库),但这只是用户有权访问您的数据库时的一个问题。如果您的应用程序是操作数据库中数据的唯一方式,这就足够了。存储过程选项更可取,但如果出于某种原因它不可行,这是您的下一个最佳选择。

异步完整性检查

最后一个选择是定期进行一些工作运行 数据完整性检查和问题报告。这不会阻止数据出错,但会帮助您快速了解它,以便您可以调查并解决它。通常我会避免这种情况,因为防止坏数据远比以后清理好;但在某些用例中,这是唯一的选择。

修改数据模型

(建议更换型号)也很好;即 projectspecial case 之间的 difference/relationship 是多少?可以将它们建模为同一事物吗?只有一个 and/or 另一个可以使用其他属性。

设置

考虑 3 tables

  • 人类

还有一个table

  • 考试

检查可能是人 xor 猫 xor 狗。

以下三种变体在我看来都是可能的:

变体 1(留在其中一个 table)

考试

  • id
  • ...
  • human_id
  • cat_id
  • dog_id

  • 检查constraints/triggers这三个值中只有一个可以为空

变体 2(创建关系 tables)

humans_examinations

  • id
  • human_id
  • examination_id

cats_examinations

  • id
  • cat_id
  • examination_id

dogs_examinations

  • id
  • dog_id
  • examination_id

变体 3(创建超类型)

患者

  • id
  • ...(可能是human/cat/dog的其他公共字段)

人类

  • id
  • ...
  • patient_id

  • id
  • ...
  • patient_id

  • id
  • ...
  • patient_id

考试

  • id
  • ...
  • patient_id