因为我正处于为我的一个项目设置数据库的最后阶段,我想到了一个需要添加到任务表的额外约束(见下图),但我不确定如何这可以在 MySQL 中实现。
原始数据库架构(无标记): Click here.
对于每个作业 (job
),都会分配一个 WBS 代码列表 (wbscodelist
)。这些列表中的每一个都包含一些适用于该作业的 WBS 代码 (wbscodeitem
)。一个例子是:
Job A uses WBS Code List #1
Job B uses WBS Code List #2
Job C uses WBS Code List #1
etc.
WBS Code List #1 has codes: [100, 105, 110, 115, 120]
WBS Code List #2 has codes: [2180, 2190]
etc.
目前,task.fk_wbsCodeItemID
是 wbscodeitem.wbsCodeItemID
的外键(橙色标记)。
我在这里面临的问题是,任务
可能会使用不适用于该工作
的 WBS 代码。
我想在 task.fk_wbsCodeItemID
中包含一个进一步的约束,以便它可以采用的值取决于 wbscodeitem.fk_wbsCodeListID
和 job.wbscodeitem。 fk_wbsCodeListID
等于该作业(以红色标记)。
如何在 MySQL 中为这个数据库模式包含这个约束?此问题是否可能是由于此数据库的当前设计造成的(我是否需要更改它)?
我知道这可能需要更多细节,因此我可以包含更多细节或在必要时进行澄清。
最佳答案
一种方法是通过受控冗余。您可以将函数依赖项 jobNumber -> fk_wbsCodeListID
反规范化为 joblocation
和 task
,并使用复合 FK 约束来防止不一致。同样,函数依赖 wbsCodeItemID -> fk_wbsCodeListID
可以反规范化为 task
。 task
中重叠的复合 FK 约束将强制执行您的要求:
CREATE TABLE `wbscodelist` (
`wbsCodeListID` int(11) NOT NULL,
`description` varchar(45) NOT NULL,
PRIMARY KEY (`wbsCodeListID`)
) ENGINE=InnoDB;
CREATE TABLE `wbscodeitem` (
`wbsCodeItemID` int(11) NOT NULL,
`wbsCode` varchar(10) NOT NULL,
`description` varchar(50) NOT NULL,
`notes` varchar(80) NOT NULL,
`fk_wbsCodeListID` int(11) NOT NULL,
PRIMARY KEY (`wbsCodeItemID`),
UNIQUE KEY (`wbsCodeItemID`,`fk_wbsCodeListID`),
KEY (`fk_wbsCodeListID`),
FOREIGN KEY (`fk_wbsCodeListID`) REFERENCES `wbscodelist` (`wbsCodeListID`) ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLE `job` (
`jobNumber` varchar(15) NOT NULL,
`jobName` varchar(45) NOT NULL,
`fk_wbsCodeListID` int(11) NOT NULL,
`isActive` bit(1) NOT NULL,
PRIMARY KEY (`jobNumber`),
UNIQUE KEY (`jobNumber`,`fk_wbsCodeListID`),
KEY (`fk_wbsCodeListID`),
FOREIGN KEY (`fk_wbsCodeListID`) REFERENCES `wbscodelist` (`wbsCodeListID`) ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLE `joblocation` (
`jobLocationID` int(11) NOT NULL,
`roomNumber` varchar(25) NOT NULL,
`fk_jobNumber` varchar(15) NOT NULL,
`fk_wbsCodeListID` int(11) NOT NULL,
PRIMARY KEY (`jobLocationID`),
KEY (`fk_jobNumber`,`fk_wbsCodeListID`),
KEY (`jobLocationID`,`fk_wbsCodeListID`),
FOREIGN KEY (`fk_jobNumber`, `fk_wbsCodeListID`) REFERENCES `job` (`jobNumber`, `fk_wbsCodeListID`) ON UPDATE CASCADE
) ENGINE=InnoDB;
CREATE TABLE `task` (
`taskID` int(11) NOT NULL,
`fk_JobLocationID` int(11) NOT NULL,
`fk_JobNumber` varchar(15) NOT NULL,
`fk_wbsCodeItemID` int(11) NOT NULL,
`fk_wbsCodeListID` int(11) NOT NULL,
PRIMARY KEY (`taskID`),
KEY (`fk_wbsCodeItemID`,`fk_wbsCodeListID`),
KEY (`fk_JobLocationID`,`fk_wbsCodeListID`),
FOREIGN KEY (`fk_JobLocationID`, `fk_wbsCodeListID`) REFERENCES `joblocation` (`jobLocationID`, `fk_wbsCodeListID`) ON UPDATE CASCADE,
FOREIGN KEY (`fk_wbsCodeItemID`, `fk_wbsCodeListID`) REFERENCES `wbscodeitem` (`wbsCodeItemID`, `fk_wbsCodeListID`) ON UPDATE CASCADE
) ENGINE=InnoDB;
注意复合索引以匹配复合 FK 约束。
另一种选择是创建触发器来检查插入/更新的 FK 值是否通过连接相关:
DELIMITER ;;
CREATE TRIGGER check_task_insert BEFORE INSERT ON task
FOR EACH ROW
BEGIN
IF NOT EXISTS (
SELECT 1
FROM joblocation loc
JOIN job ON loc.fk_jobNumber = job.jobNumber
JOIN wbscodeitem itm ON job.fk_wbsCodeListID = itm.fk_wbsCodeListID
WHERE loc.jobLocationID = new.fk_jobLocationID
AND itm.wbsCodeItemID = new.fk_wbsCodeItemID
) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'fk_wbsCodeItemID doesn\'t match fk_wbsCodeListID of associated job';
END IF;
END;
;;
CREATE TRIGGER check_task_update BEFORE UPDATE ON task
FOR EACH ROW
BEGIN
IF NOT EXISTS (
SELECT 1
FROM joblocation loc
JOIN job ON loc.fk_jobNumber = job.jobNumber
JOIN wbscodeitem itm ON job.fk_wbsCodeListID = itm.fk_wbsCodeListID
WHERE loc.jobLocationID = new.fk_jobLocationID
AND itm.wbsCodeItemID = new.fk_wbsCodeItemID
) THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'fk_wbsCodeItemID doesn\'t match fk_wbsCodeListID of associated job';
END IF;
END;
;;
DELIMITER ;
关于mysql - 如何将复杂的约束应用于 MySQL 中的数据库表?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42571843/