我有一张桌子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。
答案 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 case
和val timer = Timer()
timer.schedule(timerTask { nextScreen() }, 3000)
之间的区别/关系是什么?它们可以被建模为同样的东西;只有附加属性可用于一个和/或另一个。
答案 3 :(得分:0)
考虑3张桌子
和另一个表格
检查可能是人的xor或狗的xor。
我认为以下三种变体都是可能的:
考试
dog_id
检查约束/触发器,以确保这三个值之一只能为空
人类检查
cats_examinations
dogs_examinations
患者
人类
猫
狗
考试