Триггер базы данных: сравнение значения столбца с пустой переменной

Я готовлюсь к экзамену по моделируемой разработке. Я столкнулся с конкретным триггером базы данных:

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 в качестве СУБД.)

2 ответа

Решение

Вы упускаете тот факт, что при сравнении NULL в NULL (или к любому другому значению), ответ NULL не false, И отрицание NULL все еще NULL так что в IF заявление ELSE часть будет стрелять (если есть).

Я предлагаю вам прочитать Руководство по Firebird Null для лучшего понимания всего этого.

КАК. Делаем этот ответ ради подсветки кода.

Возможно, вы захотите изменить свой триггер, чтобы реагировать как на обновления, так и на вставки.

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

Другие вопросы по тегам