Господа, может кто-нибудь подскажет как можно решить следующую проблему — выдачу Oracle исключения
при попытке сделать update на таблицу в тригере before(after) update на эту таблицу. Это нормально например
для Interbase.
Здравствуйте Лукья, Вы писали:
Л> Сам виноват, не так сформулировал... Мне нужно не отпихнуться от исключения, а обойти это, т.е можно-ли и как, если можно, производить UPDATE TABLE set FIELD=... where ... из тригера BEFORE UPDATE
Ну, имхо, в большинстве случаев это свидетельствует о неправильно спроектированной БД. Кроме, пожалуй, одного случая — когда надо организовать рекурсивное дерево и устанавливать на уровни, имеющие подуровни, признак наличия подуровней. Хотя, наверное, есть еще другие примеры и я, может быть, погорячился о неправильном проектировании.
Так вот, ближе телу:
create table TREE(
TREE_ID NUMBER(10) default 0 not null,
PARENT_ID NUMBER(10) default 0,
IS_FOLDER CHAR(1) default 'F' not null /* только 'T','F' - допустимы */,
NAME VARCHAR2(80)
);
alter table TREE
add constraint TREE_TREE_FK foreign key (PARENT_ID)
references TREE (TREE_ID) on delete cascade;
/* хотя on delete cascade можно и пересмотреть */
Дальше пишешь триггер
before for each row:
create or replace trigger TREE_REG_UPD
BEFORE DELETE OR INSERT OR UPDATE OF PARENT_ID
ON TREE
FOR EACH ROW
BEGIN
IF INSERTING OR UPDATING THEN
PCK_TREE.REG_ID(:NEW.PARENT_ID);
END IF;
IF DELETING OR UPDATING THEN
PCK_TREE.REG_ID(:OLD.PARENT_ID);
END IF;
END;
И триггер уровня statement:
create or replace trigger TREE_SET_FOLDER
AFTER DELETE OR INSERT OR UPDATE
ON TREE
BEGIN
PCK_TREE.SET_FOLDER;
END;
Теперь осталось рассмотреть пакет, в данные которого ты регистрируешь изменяемые записи и который обновляет эти записи через SET_FOLDER:
CREATE OR REPLACE PACKAGE PCK_TREE IS
PROCEDURE REG_ID(P_ID IN NUMBER := 0);
PROCEDURE SET_FOLDER;
END PCK_TREE;
CREATE OR REPLACE PACKAGE BODY PCK_TREE IS
TYPE TTBL_ID IS TABLE OF NUMBER(10,0) INDEX BY BINARY_INTEGER;
V_RECURSED BOOLEAN := FALSE;
V_TABLE_ID TTBL_ID;
PROCEDURE REG_ID(P_ID IN NUMBER := 0) IS
BEGIN
IF P_ID IS NOT NULL THEN
V_TABLE_ID(P_ID) := P_ID;
END IF;
END;
PROCEDURE SET_FOLDER IS
V_COUNT NUMBER;
V_INDEX BINARY_INTEGER;
BEGIN
IF NOT V_RECURSED THEN
V_RECURSED := TRUE;
IF V_TABLE_ID.COUNT > 0 THEN
V_INDEX := V_TABLE_ID.FIRST;
LOOP
SELECT COUNT(*) INTO V_COUNT
FROM TREE WHERE PARENT_ID=V_TABLE_ID(V_INDEX) AND ROWNUM<2;
UPDATE TREE SET IS_FOLDER = DECODE(V_COUNT, 0, 'F', 'T')
WHERE TREE_ID=V_TABLE_ID(V_INDEX);
EXIT WHEN V_INDEX = V_TABLE_ID.LAST;
V_INDEX := V_TABLE_ID.NEXT(V_INDEX);
END LOOP;
V_TABLE_ID.DELETE;
END IF;
V_RECURSED := FALSE;
END IF;
END;
END PCK_TREE;
Про признак рекурсивности не забудь — это достаточно важно.
Идеология самого оракла такова — операторы DML над таблицей, владелицей триггера, в триггерах уровня ROW для такой таблицы
запрещены, поскольку ведут к рекурсивному вызову триггеров — что небезопасно. Но в триггерах уровня STATEMENT операции DML над таблицей-владелицей разрешены.
Никогда не бойся браться делать то, что делать не умеешь. Помни, ковчег был построен любителем. Профессионалы построили Титаник...