Опитайте сложен тригер:
CREATE OR REPLACE TRIGGER compound_trigger_name
FOR INSERT OR UPDATE OF salary ON treballa
COMPOUND TRIGGER
TYPE Departments_t IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
Departments Departments_t;
BEFORE EACH ROW IS
BEGIN
-- collect updated or inserted departments
Departments( :new.department ) := :new.department;
END BEFORE EACH ROW;
AFTER STATEMENT IS
sum_sal NUMBER;
BEGIN
-- for each updated department check the restriction
FOR dept IN Departments.FIRST .. Departments.LAST
LOOP
SELECT sum(salary) INTO sum_sal FROM treballa WHERE department = dept;
IF sum_sal > 1000 THEN
raise_application_error(-20123, 'The total salary for department '||dept||' cannot exceed 1000');
END IF;
END LOOP;
END AFTER STATEMENT;
END compound_trigger_name;
/
========РЕДАКТИРАНЕ - няколко въпроса и отговора ===========
В:Защо възниква грешка в променящата се таблица?
О:Това е описано в документацията:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708
В:как да избегнете грешка в променящата се таблица?
О:Документацията препоръчва използването на задействане на съединение, вижте това:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ
В:Какво е комбиниран тригер и как работи?
О:Това е огромна тема, моля, вижте документацията тук:http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD
Накратко:това е специален вид тригер, който прави psiспособен да комбинира четири типа отделни тригери:BEFORE statement
, BEFORE-for each row
, AFTER for each row
и AFTER statament
в една декларация. Това улеснява прилагането на някои сценарии, при които има нужда от предаване на някои данни от един тригер към друг. Моля, разгледайте връзката по-горе за повече подробности.
В:Но какво всъщност означава "Departments( :new.department ) := :new.department;
?
О:Тази декларация съхранява номер на отдел в асоциативен масив.
Този масив е деклариран в декларативна част на съставния тригер:
TYPE Departments_t IS TABLE OF treballa.department%TYPE INDEX BY varchar2(100);
Departments Departments_t;
В документацията, свързана със съставните тригери, се казва, че:http ://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHJBEFE
Горното означава, че Departments
променливата се инициализира само веднъж в началото на цялата обработка, точно след задействане на тригера. „Продължителност на командата за задействане“ означава, че тази променлива се унищожава, след като тригерът приключи.
Този израз:Departments( :new.department ) := :new.department;
съхранява номер на отдел в асоциативния масив. Намира се в BEFORE EACH ROW
раздел, след което се изпълнява за всеки ред, който е актуализиран (или вмъкнат) от израза update/insert.:new
и :old
са псевдозаписи, повече за тях можете да намерите тук: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/triggers.htm#LNPLS99955
Накратко::new.department
извлича нова стойност на department
колона- за текущо актуализиран ред (актуализирана стойност - СЛЕД актуализацията), докато :old.department
дава стара стойност на тази колона (ПРЕДИ актуализацията).
Тази колекция по-късно се използва в AFTER STATEMENT
, когато тригерите изберат всички актуализирани отдели (в FOR-LOOP), за всеки отдел задейства SELECT SUM(salary) ...
и след това проверява дали тази сума е по-малка от 1000
Помислете за проста актуализация:UPDATE treballa SET salary = salary + 10
. Това е единичен оператор за актуализиране, но променя много редове наведнъж. Редът на изпълнение на нашия тригер е следният:
- Инструкцията за актуализация се задейства:
UPDATE treballa SET salary = salary + 10
- Изпълнява се декларативният раздел на тригера, тоест:
Departments
променливата е инициализирана BEFORE EACH ROW
секция се изпълнява отделно за всеки актуализиран ред - толкова пъти, колкото редове има за актуализиране. На това място събираме всички отдели от променени редове.AFTER STATEMENT
секция се изпълнява. В този момент таблицата вече е актуализирана - всички редове вече имат нови, актуализирани заплати. Ние преглеждаме отделите, записани вDepartments
и за всеки проверяваме дали сумата на заплатите е по-малка или равна на 1000. Ако тази сума е> 1000 за някой от тези отдели, тогава се хвърля грешка и цялата актуализация се прекъсва и се връща назад. В противен случай тригерът завършва и актуализацията е направена (но все пак трябва да извършите тези промени).
В:Какво е асоциативен масив и защо се използва само този вид колекция, а не други колекции (променлив или вложена таблица)?
О:PL/SQL колекциите са огромна тема. Следвайте тази връзка, за да ги научите:http:// docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005
Накратко - асоциативният масив (или таблица с индекси) е като карта в java (hashmap, treemap и т.н.) - това е набор от двойки ключ-стойност и всеки ключ е уникалната . Можете да поставите един и същ ключ много пъти в този масив (с различни стойности), но този ключ ще бъде съхранен само веднъж - той е уникален.
Използвах го, за да получа уникален набор от отдели.
Разгледайте нашия пример за актуализиране отново:UPDATE treballa SET salary = salary + 10
- тази команда засяга стотици редове, които имат същия отдел. Не искам колекция с един и същи отдел да се дублира 100 пъти, имам нужда от уникален набор от отдели и искам да изпълня нашата заявка SELECT sum()...
само веднъж за всеки отдел, а не 100 пъти. С помощта на sssociative array това става автоматично - получавам уникален набор от deparments.