2016-08-20 113 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