Триггер базы данных: сравнение значения столбца с пустой переменной
Я готовлюсь к экзамену по моделируемой разработке. Я столкнулся с конкретным триггером базы данных:
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