Oracle после удаления триггера ... Как избежать Mutating Table (ORA-04091)? - PullRequest
5 голосов
/ 27 апреля 2011

Допустим, у нас есть следующие структуры таблиц:

documents      docmentStatusHistory      status
+---------+   +--------------------+    +----------+
| docId   |   | docStatusHistoryId |    | statusId |
+---------+   +--------------------+    +----------+
| ...     |   | docId              |    | ...      |
+---------+   | statusId           |    +----------+
              | ...                |
              +--------------------+

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

Система медленно, но верно снижала производительность, и я предложил изменить приведенную выше структуру на:

documents           docmentStatusHistory      status
+--------------+   +--------------------+    +----------+
| docId        |   | docStatusHistoryId |    | statusId |
+--------------+   +--------------------+    +----------+
| currStatusId |   | docId              |    | ...      |
| ...          |   | statusId           |    +----------+
+--------------+   | ...                |
                   +--------------------+

Таким образом, у нас будет текущий статус документа именно там, где и должен быть.

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

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

Я создал триггер, который обновляет текущее состояние документа каждый раз, когда новый статус добавляется в историю состояний, и этоработает как заклинание.

Однако в неясной и редко используемой ситуации возникает необходимость УДАЛИТЬ последняя история статуса, а не просто добавление новой.Итак, я создал следующий триггер:

create or replace trigger trgD_History
 after delete on documentStatusHistory
 for each row
 currentStatusId number;
begin

  select statusId
    into currentStatusId
    from documentStatusHistory
   where docStatusHistoryId = (select max(docStatusHistoryId)
                                 from documentStatusHistory
                                where docId = :old.docId);

  update documentos
     set currStatusId = currentStatusId
   where docId = :old.docId;
end;

И вот где я получил печально известную ошибку ORA-04091.

Я понимаю ПОЧЕМУ Я получаю эту ошибку, хотя я настроил триггер как ПОСЛЕ триггера.

Дело в том, что я не вижу пути обхода этой ошибки.Я некоторое время искал в сети и пока не нашел ничего полезного.

Со временем мы используем Oracle 9i.

Ответы [ 2 ]

9 голосов
/ 27 апреля 2011

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

  • пакета с набором ключей (например, docId в данном случае).Временная таблица также будет работать
  • Триггер оператора before, который инициализирует коллекцию
  • Триггер уровня строки, который заполняет коллекцию каждым измененным docId
  • Оператор afterтриггер, который перебирает коллекцию и выполняет актуальное ОБНОВЛЕНИЕ

Так что-то вроде

CREATE OR REPLACE PACKAGE pkg_document_status
AS
  TYPE typ_changed_docids IS TABLE OF documentos.docId%type;
  changed_docids typ_changed_docids := new typ_changed_docids ();

  <<other methods>>
END;

CREATE OR REPLACE TRIGGER trg_init_collection
  BEFORE DELETE ON documentStatusHistory
BEGIN
  pkg_document_status.changed_docids.delete();
END;

CREATE OR REPLACE TRIGGER trg_populate_collection
  BEFORE DELETE ON documentStatusHistory
  FOR EACH ROW
BEGIN
  pkg_document_status.changed_docids.extend();
  pkg_document_status.changed_docids( pkg_document_status.changed_docids.count() ) := :old.docId;
END;

CREATE OR REPLACE TRIGGER trg_use_collection
  AFTER DELETE ON documentStatusHistory
BEGIN
  FOR i IN 1 .. pkg_document_status.changed_docids.count()
  LOOP
    <<fix the current status for pkg_document_status.changed_docids(i) >>
  END LOOP;
  pkg_document_status.changed_docids.delete();
END;
1 голос
/ 27 апреля 2011

кажется дубликатом этого вопроса

проверить Том Кайт принимает это

...