两个外键,其中一个不是 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 的所有代码,则可以在数据库外部添加验证。同样,您的验证可以被绕过(即直接进入数据库),但这只是用户有权访问您的数据库时的一个问题。如果您的应用程序是操作数据库中数据的唯一方式,这就足够了。存储过程选项更可取,但如果出于某种原因它不可行,这是您的下一个最佳选择。
异步完整性检查
最后一个选择是定期进行一些工作运行 数据完整性检查和问题报告。这不会阻止数据出错,但会帮助您快速了解它,以便您可以调查并解决它。通常我会避免这种情况,因为防止坏数据远比以后清理好;但在某些用例中,这是唯一的选择。
修改数据模型
(建议更换型号)也很好;即 project
和 special 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
我有一个 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 只需要一个引用超类型的不可空外键。
每
由于您明确要求除触发器之外的选项,这里有一些替代选项:
存储过程
使用存储过程在此 table 中执行插入操作/在此处包含验证逻辑。
这确保通过存储过程插入/更新的任何内容都受到保护;但有一个问题,即有人直接将数据插入 table.
可能会绕过此验证可以通过使用安全性来缓解该问题,以确保只有存储过程有权插入此 table/更新这些字段。有关设置此安全性的更多详细信息,请参阅此答案:
客户代码
类似于存储过程选项;如果您拥有将执行这些 inserts/updates 的所有代码,则可以在数据库外部添加验证。同样,您的验证可以被绕过(即直接进入数据库),但这只是用户有权访问您的数据库时的一个问题。如果您的应用程序是操作数据库中数据的唯一方式,这就足够了。存储过程选项更可取,但如果出于某种原因它不可行,这是您的下一个最佳选择。
异步完整性检查
最后一个选择是定期进行一些工作运行 数据完整性检查和问题报告。这不会阻止数据出错,但会帮助您快速了解它,以便您可以调查并解决它。通常我会避免这种情况,因为防止坏数据远比以后清理好;但在某些用例中,这是唯一的选择。
修改数据模型
project
和 special 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