2016-08-20 112 views
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_toNULL),事实上,经过测试,它确实可以正常工作。

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

我在这里错过了什么?

(显示的SQL应该是SQL:2003兼容的MDD工具是Cathedron,它使用火鸟作为RDBMS)

+1

_“这个触发器是为了防止输入其中经理向自己报告。“_你确定吗?这种触发似乎是为了防止经理向另一家公司的某个人报告输入而编写的。 – pilcrow

+0

@pilcrow好赶上!编辑。 –

回答

6

你错过了一个事实,当你比较NULLNULL(或到任何其他值),答案是NULL,而不是false。并且NULL的否定仍然是NULL,因此在IF声明中ELSE部分会触发(如果有的话)。

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

+0

所以,可以肯定的是:在返回空表的select之后,放入变量'v_company_name'的值是NULL,而不是某种(其他)未定义的值? –

+1

当select语句返回空结果集时,“v_company_name”的值保持不变(它不会设置为NULL!)。所以它会在执行select语句之前拥有它的值,在这种情况下应该是NULL。 – ain

+0

你是对的。在你的链接中:“如果你在一个存储过程或触发器中声明了一个变量,那么它的值是未定义的,它的状态从创建时刻开始直到某个值被赋值为NULL。” –

2

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

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

CREATE TRIGGER tManager_bi 
FOR Manager BEFORE INSERT OR UPDATE AS 
... 

如果您不需要特定的异常标识符,您也可以避免手写触发器。

你可能只需要使用SQL检查约束是

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