我准备参加模型驱动开发考试。我遇到了一个特定的数据库触发器:
CREATE TRIGGER tManager_bi
FOR Manager BEFORE INSERT AS
DECLARE VARIABLE v_company_name CHAR(30);
BEGIN
SELECT M.company
FROM Manager M
WHERE M.nr = NEW.reports_to
INTO :v_company_name;
IF (NOT(NEW.company = v_company_name))
THEN EXCEPTION eReportsNotOwnCompany;
END
此触发器旨在防止经理向外部经理报告的输入,即不是来自同一公司的经理。相应的OCL约束是:
context Manager
inv: self.company = self.reports_to.company
相关表格看起来像(简化):
CREATE TABLE Manager
(
nr INTEGER NOT NULL,
company VARCHAR(50) NOT NULL,
reports_to INTEGER,
PRIMARY KEY (nr),
FOREIGN KEY (reports_to) REFERENCES Manager (nr)
);
教科书说,当新插入的经理没有向任何人报告时(即NEW.reports_to
是NULL
),此触发器也能正常工作,实际上,经过测试,它确实有效正确。
但我不明白这一点。如果NEW.reports_to
为NULL
,那意味着变量v_company_name
将为空(未初始化?NULL
?),这意味着比较NEW.company = v_company_name
将返回false
,导致抛出异常,对吧?
我在这里缺少什么?
(显示的SQL应该符合SQL:2003标准.MDD工具是Cathedron,它使用Firebird作为RDBMS。)
答案 0 :(得分:6)
您错过了将NULL
与NULL
(或任何其他值)进行比较的事实,答案为NULL
,而不是false
。 NULL
的否定仍为NULL
,因此在IF
语句中ELSE
部分会触发(如果有的话)。
我建议你阅读Firebird Null Guide以便更好地理解这一切。
答案 1 :(得分:2)
AS。为了代码突出显示这个答案。
您可能希望修改触发器以响应更新和插入。
CREATE TRIGGER tManager_bi
FOR Manager BEFORE INSERT OR UPDATE AS
...
如果您不需要该特定的异常标识符,您也可以完全避免手写触发器。
您可以只使用SQL Check Constraint
alter table Manager
add constraint chk_ManagerNotRespondsOneself
CHECK ( NOT EXISTS (
SELECT * FROM Manager M
WHERE M.nr = reports_to
AND M.company = company
) )
现在看起来无法通过CHECK约束指定自定义异常...... http://tracker.firebirdsql.org/browse/CORE-1852