Поскольку таблица при многострочной вставке, фактически, мутирует (изменяется), можно получить разные результаты при выполнении точно такого же оператора , в зависимости от порядка строк в подзапросе (например: оператор insert при стоимостной оптимизации может дать другой результат, чемпри запроса оптимизации на основе правил).
Решение проблемы мутирования:
Шаг 1. Создаем дополнительную таблицу (можно временную), в которую при добавлении (удалении, изменении) основной таблицы будут добавляться нужные данные (например уникальный идентификатор строки основной таблицы).
Шаг 2. Создаем в основной таблице строчный триггер, который будет добавлять данные во временную таблицу, созданную на шаге 1.
Шаг 3. Создаем в основной таблице операторный триггер, который срабатывает после всех строчных триггеров и выполняет нужные действия над данными в промежуточной таблице, а затем удаляет их. Операторный триггер основной таблицы в этом случае может смело выполнять любые действия над данными своей таблицы.
Пример: есть таблица peoples c именами людей. Требуется при добавлении записи удалять другие записи, в которых встречается такое же имя.
В случае с конкретной базой данных мутирование происходит потому, что некоторые сотрудники могут иметь один и тотже код должности (занимать одну и туже должность). Поэтому при обновлении зарплаты мы получаем что в некоторых случаях должны изменится несколько записей. Для обхода подобной ситуации можно обойтись и глобальной переменной вместо дополнительной таблицы.
Код решения:
create or replace PACKAGE pk_emp_mgr AS
PROCEDURE set_start;
PROCEDURE add_comm(o_id Number, numb Number);
PROCEDURE check_undone;
END pk_emp_mgr;
CREATE OR REPLACE PACKAGE BODY pk_emp_mgr AS
und number;
iid Number;
w_t varchar2(2);
PROCEDURE set_start IS
BEGIN
und:=0;
iid:=0;
END set_start;
PROCEDURE add_comm(o_id Number, numb Number) IS
BEGIN
iid:=o_id;
und:=numb;
END add_comm;
PROCEDURE check_undone IS
emp number;
hou number;
CURSOR c_1 IS SELECT object_id FROM params WHERE number_value=iid and attr_id=11;
c_2 c_1%ROWTYPE;
BEGIN
emp:=iid;
OPEN c_1;
LOOP
FETCH c_1 into c_2 ;
EXIT WHEN c_1%NOTFOUND;
select text_value into w_t from params where object_id=emp and attr_id=6;
dbms_output.put_line(w_t);
IF w_t='F' THEN
BEGIN
UPDATE params SET number_value=und*0.76 WHERE attr_id=5 and object_id=c_2.object_id;
und:=und/0.76;
END;
ELSE
BEGIN
select number_value into hou from params where object_id=c_2.object_id and attr_id = 4;
und:=und*hou;
UPDATE params SET number_value=und*0.76 WHERE attr_id=5 and object_id=c_2.object_id;
und:=und/0.76;
und:=und/hou;
END;
END IF;
END LOOP;
CLOSE c_1;
END check_undone;
END pk_emp_mgr;
CREATE OR REPLACE TRIGGER "EMP_COMM" AFTER UPDATE ON params
DECLARE
BEGIN
pk_emp_mgr.check_undone;
END;
CREATE OR REPLACE TRIGGER "TRG_TR_AR"
after update
on params
FOR EACH ROW
Begin
IF :new.attr_id=7 THEN
pk_emp_mgr.add_comm(:new.object_id,:new.number_value);
end if;
end;
CREATE OR REPLACE TRIGGER "TRG_TR_BS"
before update
on params
begin
pk_emp_mgr.set_start;
end;
Код работает следующим образом:
1) В операторном триггере который срабатывает (или возбуждается, как написано в оригинальной инструкции к СУБД) before update выполняется зануление всех глобальный переменных;
2) В строчном триггере в эти глобальные переменные заносятся данные;
3) В операторном триггере создается курсор в который заносятся коды сотрудников которые соотвествуют коду должности в которой мы поменяли зарплату и в цыкле происходит обновление записи.
7.4 Заполнение таблиц метамодели
delete from params;
delete from objects;
delete from attributes;
delete from object_types;
/* Types */
insert into object_types values (1, 'emp', 'Employee');
insert into object_types values (2, 'job', 'Job');
insert into object_types values (3, 'dept', 'Department');
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.