3

我正在准备关于模型驱动开发的考试。我遇到了一个特定的数据库触发器:

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_tois NULL),此触发器也将正常工作,并且实际上,在测试时,它确实可以正常工作。

但我不明白这一点。如果NEW.reports_tois NULL,这意味着变量v_company_name将为空(未初始化??NULL),这意味着比较NEW.company = v_company_name将返回false,导致抛出异常,对吗?

我在这里想念什么?

(显示的 SQL 应该符合 SQL:2003。MDD 工具是 Catedron,它使用 Firebird 作为 RDBMS。)

4

2 回答 2

6

您错过了这样一个事实,即当您与NULLNULL或任何其他值)进行比较时,答案是NULL,不是falseNULL并且对is still的否定NULL,因此在IF声明中该ELSE部分会触发(如果有的话)。

我建议您阅读Firebird Null Guide以更好地理解这一切。

于 2016-08-20T18:38:12.680 回答
2

作为。为了代码突出显示而做出这个答案。

您可能希望修改触发器以响应更新和插入。

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

于 2016-08-22T10:02:20.877 回答