2016-05-22 2 views
0

У меня есть таблица со следующей структурой:Update Триггер PL/SQL Oracle

create table treballa (
code varchar2(4), 
name varchar2(20), 
director varchar2(4), 
department number, 
salary int, 
primary key (code), 
foreign key (director) references treballa(code) 
) 

И мне нужно создать 1 триггер, который проверяет, является ли отдел номер обновленного работника, общая зарплата между всеми рабочими этого отдела не превышает 10000, но я не знаю, как это сделать ... Не могли бы вы мне помочь? Большое спасибо.

РЕДАКТИРОВАТЬ:

CREATE OR REPLACE TRIGGER controlsalaridept BEFORE INSERT OR UPDATE ON TREBALLA 
FOR EACH ROW 
    DECLARE 
    salaritotal INT := 0; 
    BEGIN 
    IF INSERTING THEN 
     SELECT sum(salary) INTO salaritotal FROM TREBALLA WHERE DEPARTMENT LIKE :new.DEPARTAMENT; 
     DBMS_OUTPUT.PUT_LINE('Salari Total abans suma:'||salaritotal); 
     salaritotal := salaritotal + :new.SALARY; 
     DBMS_OUTPUT.PUT_LINE('Salari Total després:'||salaritotal); 
     IF salaritotal > 10000 THEN 
     raise_application_error(-20025, 'La suma del salari total de cada departament supera els 10000 Euros'); 
     END IF; 
    END IF; 
    IF UPDATING THEN 
     SELECT sum(salary) INTO salaritotal FROM TREBALLA WHERE DEPARTMENT LIKE :old.DEPARTAMENT; 
     DBMS_OUTPUT.PUT_LINE('Salari Total abans suma:'||salaritotal); 
     salaritotal := salaritotal - :old.SALARY + :new.SALARY; 
     DBMS_OUTPUT.PUT_LINE('Salari Total després:'||salaritotal); 
     IF salaritotal > 10000 THEN 
     raise_application_error(-20026,'La suma del salari total de cada departament supera els 10000 Euros'); 
     END IF; 
    END IF; 
    END; 

КОНЕЦ РЕДАКТИРОВАТЬ/

ОШИБКА ПРИ ОБНОВЛЕНИЕ:

[42000] [4091] ОРА-04091: таблица SPECIAL.TREBALLA мутирует, триггер/функция не может видеть это ORA-06512: at «SPECIAL.CONTROLSALARIDEPT», строка 14 ORA-04088: ошибка во время выполнения триггера «SPECIAL.CONTROLSALARIDEPT»

PD: Извините, я очень новичок в оракуле, и мне нужна помощь с этим триггером, я не знаю, даже если это правильно, что я делаю .... Первая часть триггера " ЕСЛИ ВСТАВКА»хорошо работает, проблема с ОБНОВЛЕНИЕМ ...

+0

Это для реального применения? Или домашнее задание? Предполагая, что это домашнее задание, рассказал ли ваш инструктор об исключении мутирующей таблицы? Это невозможно реализовать с помощью одного триггера, что было бы крайне неприемлемым в реальной системе. Чтобы сделать это правильно, вам понадобится несколько триггеров (или составной триггер с несколькими разделами). –

+0

Является домашним заданием, учитель не говорил об этом, когда я пытаюсь сделать этот триггер по-своему, Oracle дает мне ошибку, похожую на «таблица мутирует ...» Извините, я не на переднем плане ПК теперь, но мне просто нужно знать, какой рабочий был отредактирован, чтобы снова пересчитать зарплату ... –

+0

Измените свой вопрос, включив триггер, который вы написали, и полученную вами ошибку. –

ответ

1

Попробуйте составной триггер:

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; 
/

======== EDIT - несколько вопросов и ответы ===========


В: Почему возникает ошибка с изменением таблицы?
A: Это описано в документации:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#g1699708

Trigger Ограничения на мутирует таблицах
Таблица Mutating представляет собой таблицу , которая модифицируется с помощью UPDATE, DELETE или INSERT заявление , или таблицу , которая может быть обновлена ​​эффектами ограничения DELETE CASCADE .

Сессия, которая выдала триггерный оператор, не может запросить или изменить мутирующую таблицу. Это ограничение предотвращает триггер от , видя несогласованный набор данных.

Данное ограничение распространяется на все триггеры, которые используют предложение FOR EACH ROW . Представления, измененные в триггерах INSTEAD OF, не считаются мутированием.

Когда триггер встречает таблицу мутирует, возникает ошибка времени выполнения, эффекты тела триггера и триггера прокатывают назад, и управление возвращается к пользователю или приложению. (Вы можете использовать составные триггеры , чтобы избежать ошибки с ошибкой.Для получения дополнительной информации приведена в разделе Использование соединения Триггеры избежать мутирует СТОЛ Error)


. Q: Как избежать ошибки Mutating таблицы?
A: документация рекомендует использовать триггер coumpound, увидеть это: http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CHDFEBFJ

Используя соединение Триггеры, чтобы избежать Mutating-Table Ошибка Вы можете использовать соединение триггеров, чтобы избежать ошибки Mutating-таблицы (ORA-04091) , описанный в разделе Ограничения триггера на мутационных таблицах.


Q: Что такое триггер соединение и как это работает?
A: Это огромная тема, пожалуйста, обратитесь к документации здесь: http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/triggers.htm#CIHEFGFD

Короче говоря: это особый вид триггера, который делает psiible объединить четыре типа отдельных триггеров: BEFORE statement, BEFORE-for each row, AFTER for each row и AFTER statament в одно объявление. Это упрощает реализацию некоторых сценариев, в которых необходимо передавать некоторые данные с одного триггера на другой. Пожалуйста, изучите приведенную выше ссылку для получения более подробной информации.


Q: Но что на самом деле делает "Departments(:new.department) := :new.department;?
A: В этом объявлении хранится номер отдела в ассоциативный массив.

Этот массив объявлен в декларативной части составного триггера:

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, когда TRIGERS выбрать все обновленные отделы (в FOR-LOOP), для каждого departmens пожаров SELECT SUM(salary) ... и затем проверяет, если эта сумма меньше, чем 1000

Рассмотрим простое обновление: UPDATE treballa SET salary = salary + 10. Это один оператор обновления, но он меняет сразу несколько строк. Порядок исполнения нашего триггера следующим образом:

  1. statament обновления обжигают: UPDATE treballa SET salary = salary + 10
  2. декларативная часть триггера выполняется, то есть: Departments переменные инициализируются
  3. BEFORE EACH ROW секции выполняются , отдельно для каждой обновленной строки - столько раз, сколько есть строк для обновления. В этом месте мы собираем все отделы из измененных строк.
  4. AFTER STATEMENT раздел выполнен. На этом этапе таблица уже обновлена ​​- все строки уже имеют новые обновленные зарплаты. Мы проводим цикл в отделах, сохраненных в Departments, и для каждого из них мы проверяем, равна ли сумма зарплат меньше или равна 1000. Если эта сумма составляет> 1000 для любого из этих отделов, тогда возникает ошибка, и все обновление прерывается и свернуто назад. В противном случае триггер завершится, и обновление будет выполнено (но вам все равно необходимо зафиксировать эти изменения).

Q: Что представляет собой ассоциативный массив, и почему именно используется этот вид коллекции, а не других коллекций (в VARRAY или вложенной таблицы)?
A: Коллекции PL/SQL - огромная тема. Перейдите по этой ссылке, чтобы узнать их: http://docs.oracle.com/cd/E11882_01/appdev.112/e25519/composites.htm#LNPLS005

Короче - ассоциативный массив (или индекс-таблицы), как карты в Java (HashMap, древовидные карты и т.д.) - это набор пар ключ-значение, и каждый ключ уникален. Вы можете поместить один и тот же ключ много раз в этот массив (с разными значениями), но этот ключ будет храниться только один раз - он уникален.
Я использовал его, чтобы получить уникальный набор отделов.
Рассмотрим пример нашего обновления: UPDATE treballa SET salary = salary + 10 - эта команда касается сотен строк, имеющих один и тот же отдел. Я не хочу, чтобы коллекция с тем же отделом дублировалась 100 раз, мне нужен уникальный набор отделов, и я хочу выполнить наш запрос SELECT sum()... только один раз для каждого отдела, а не 100 раз. С помощью sssociative массива это делается автоматически - я получаю уникальный набор отступов.

+0

Это работает! Большое спасибо Кордирко, но мне нужно переустановить его, чтобы я знал, как это сделать сам. В этом случае вы создадите временную таблицу Departments_t, чтобы скопировать ее. Но что на самом деле делает «Отделы (: new.department): =: new.department;» ? Я не понимаю, как вы собираете информацию или где вы ее положили на самом деле ... Спасибо –

+0

Я приложил короткое объяснение к ответу, пожалуйста, взгляните на него. – krokodilko

+0

Большое спасибо, я очень ценю ваши усилия, я обнаружил составные триггеры и мутирующие таблицы. Это первый случай, когда я спрашиваю что-то о stackoverflow, потому что обычно я ищу, пока не нахожу ответ, но на этот раз я не знал, откуда мне нужно начинать поиск. Спасибо kordirko за все ваше время, ваши знания и ваши усилия . Желаю вам отличного дня! –

Смежные вопросы