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

时间:2017-07-27 11:31:40

标签: mysql sql database-design innodb mysql-5.5

我有一张桌子time。时间条目( 1:n关系)要么属于project条目,要么属于special_work条目。必须同时设置项目ID或special_work id(独占或)。

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中写这个?除了触发器之外的任何方式?

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

我的数据库是Mysql 5.5 InnoDB。

4 个答案:

答案 0 :(得分:4)

这样的互斥引用可能表示您的设计中缺少“超类型”表。考虑创建一个代表所有“工作”的新超类型表 - 包括项目和特殊工作。项目和特殊工作表将引用超类型表,一对一,其中包含所有项目/工作标识符的并集。然后,Time表只需要一个引用超类型的非可空外键。

答案 1 :(得分:3)

您的数据模型很好。在大多数数据库中,您还需要添加check约束:

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

但是,MySQL不支持检查约束。如果您愿意,可以使用触发器实现逻辑。

答案 2 :(得分:1)

@GordonLinoff's answer;在MySql中,触发器是实现此约束的最佳方式。您是否有特定原因不想使用触发器?

由于您已明确要求触发器以外的选项,因此以下是一些备用选项:

存储过程

使用存储过程对此表执行插入/在此处包含验证逻辑。

这确保了通过存储过程插入/更新的任何内容都受到保护;但是有一个问题是,有人可能会直接将数据插入表格中来绕过此验证。

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

客户代码

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

异步完整性检查

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

修改数据模型

@SqlVogel's answer(建议改变模型)也很出色;即special caseval timer = Timer() timer.schedule(timerTask { nextScreen() }, 3000) 之间的区别/关系是什么?它们可以被建模为同样的东西;只有附加属性可用于一个和/或另一个。

答案 3 :(得分:0)

设置

考虑3张桌子

  • 人类

和另一个表格

  • 考试

检查可能是人的xor或狗的xor。

我认为以下三种变体都是可能的:

变体1(将其保留在一张表中)

考试

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

  • 检查约束/触发器,以确保这三个值之一只能为空

变体2(创建关系表)

人类检查

  • id
  • human_id
  • examination_id

cats_examinations

  • id
  • cat_id
  • examination_id

dogs_examinations

  • id
  • dog_id
  • examination_id

变体3(创建超类型)

患者

  • id
  • ...(可能是人类/猫/狗的其他常见领域)

人类

  • id
  • ...
  • patient_id

  • id
  • ...
  • patient_id

  • id
  • ...
  • patient_id

考试

  • id
  • ...
  • patient_id