I created a trigger as below:
CREATE OR REPLACE TRIGGER trigger_test
AFTER INSERT ON trigger_1
FOR EACH ROW
DECLARE
t_identifier VARCHAR2(10);
t_name VARCHAR2(20);
BEGIN
t_identifier := (:NEW.IDENTIFIER);
t_name := (:NEW.NAME);
INSERT INTO trigger_2(IDENTIFIER,NAME)VALUES(t_identifier,t_name);
COMMIT;
END;
I am trying to insert a row in trigger_1
INSERT INTO trigger_1(IDENTIFIER,NAME)
VALUES('1234567','Vijay');
It is giving me the errors:
ORA-04092: cannot COMMIT in a trigger
ORA-06512: at "LVSDBO46.TRIGGER_TEST", line 8
ORA-04088: error during execution of trigger 'LVSDBO46.TRIGGER_TEST'
Could nybody please help?
Одна из моих функций обновления не может быть выполнена, потому что приведенный ниже триггер выдает исключение ORA-2001, ORA-06512 и ORA-04088.
create or replace TRIGGER "UAM_USER"."BEFORE_UPDATE_VILLAGE" BEFORE UPDATE ON village
FOR EACH ROW
DECLARE
v_tbl_id NUMBER(2);
user_xcep EXCEPTION;
PRAGMA EXCEPTION_INIT( user_xcep, -20001 );
BEGIN
IF :OLD.panchayat_id != :NEW.panchayat_id
THEN
RAISE user_xcep;
END IF;
v_tbl_id := 14;
IF :OLD.name != :NEW.name
THEN
INSERT INTO UAM_USER.MASTER_HISTORY
(HISTORY_ID
,TBL_ID
,USER_ID
,FIELD_NAME
,OLD_VALUE
,NEW_VALUE
,HISTORY_DATE
,USER_NAME
,record_id)
VALUES
(MASTER_HISTORY_SEQ.NEXTVAL
,v_tbl_id
,:NEW.UPDATE_BY
,'Name'
,:OLD.name
,:NEW.name
,:NEW.UPDATE_DATE
,:NEW.update_by_name
,:NEW.village_id
);
END IF;
IF (:OLD.name_hindi IS NULL AND :NEW.name_hindi IS NOT NULL)
OR (:NEW.name_hindi IS NULL AND :OLD.name_hindi IS NOT NULL)
OR (:OLD.name_hindi != :NEW.name_hindi)
THEN
INSERT INTO UAM_USER.MASTER_HISTORY
(HISTORY_ID
,TBL_ID
,USER_ID
,FIELD_NAME
,OLD_VALUE
,NEW_VALUE
,HISTORY_DATE
,USER_NAME
,record_id)
VALUES
(MASTER_HISTORY_SEQ.NEXTVAL
,v_tbl_id
,:NEW.UPDATE_BY
,'Name Hindi'
,:OLD.name_hindi
,:NEW.name_hindi
,:NEW.UPDATE_DATE
,:NEW.update_by_name
,:NEW.village_id
);
END IF;
IF (:OLD.status IS NULL AND :NEW.status IS NOT NULL)
OR (:NEW.status IS NULL AND :OLD.status IS NOT NULL)
OR (:OLD.status != :NEW.status)
THEN
INSERT INTO UAM_USER.MASTER_HISTORY
(HISTORY_ID
,TBL_ID
,USER_ID
,FIELD_NAME
,OLD_VALUE
,NEW_VALUE
,HISTORY_DATE
,USER_NAME
,record_id)
VALUES
(MASTER_HISTORY_SEQ.NEXTVAL
,v_tbl_id
,:NEW.UPDATE_BY
,'Status'
,:OLD.status
,:NEW.status
,:NEW.UPDATE_DATE
,:NEW.update_by_name
,:NEW.village_id
);
END IF;
END;
ЖУРНАЛ КОНСОЛИ:
org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [ UPDATE village SET name = ? ,panchayat_id = ? ,name_hindi = ? ,status = ? ,update_date = ? ,update_by = ? ,update_by_name = ? ,dc_id = ? ,loksabha_id = ? ,census_code = ? ,pincode = ? ,area_type = ? WHERE village_id = ? ]; SQL state [72000]; error code [20001]; ORA-20001:
ORA-06512: at "UAM_USER.BEFORE_UPDATE_VILLAGE", line 9
ORA-04088: error during execution of trigger 'UAM_USER.BEFORE_UPDATE_VILLAGE'
; nested exception is java.sql.SQLException: ORA-20001:
ORA-06512: at "UAM_USER.BEFORE_UPDATE_VILLAGE", line 9
ORA-04088: error during execution of trigger 'UAM_USER.BEFORE_UPDATE_VILLAGE'
Я не могу понять это, я проверяю все свои параметры в своем контроллере, все параметры в порядке, и в моем контроллере и сервисе нет проблем, проблема выше триггера и согласно моим исследованиям и разработкам коды ошибок {{X0} } и ORA-06512
представляют ошибку относительно ширины столбца, а ORA-4088
представляют рекурсивное обновление.
ОБНОВЛЕНИЕ КОДА:
@Override
public void update(Village village) {
String q = " UPDATE village "
+ " SET name = :name "
// + " ,panchayat_id = :panchayat_id "
+ " ,name_hindi = :name_hindi "
+ " ,status = :status "
+ " ,update_date = :update_date "
+ " ,update_by = :update_by "
+ " ,update_by_name = :update_by_name "
+ " ,dc_id = :dc_id "
+ " ,loksabha_id = :loksabha_id "
+ " ,census_code = :census_code "
+ " ,pincode = :pincode "
+ " ,area_type = :area_type "
+ " WHERE village_id = :village_id ";
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("village_id", village.getVillageId());
param.addValue("panchayat_id", village.getPanchayat() != null ? village.getPanchayat().getPanchayatId() : null);
param.addValue("name", village.getName());
param.addValue("name_hindi", village.getNameHindi());
param.addValue("status", village.getStatus());
param.addValue("update_date", village.getUpdateDate());
param.addValue("update_by", village.getUserdetail() != null ? village.getUserdetail().getUserId() : null);
param.addValue("update_by_name", village.getUserdetail().getName());
param.addValue("dc_id", village.getDc() != null ? village.getDc().getDcId() : null);
param.addValue("loksabha_id", village.getLoksabha() != null ? village.getLoksabha().getLoksabhaId() : null);
param.addValue("census_code", village.getCensusCode());
param.addValue("pincode", village.getPincode());
param.addValue("area_type", village.getAreaType());
getNamedParameterJdbcTemplate().update(q, param);
}
Я проверяю свои столбцы, все в порядке, поэтому все, что осталось, это ORA-04088, и я не могу понять, где и как он рекурсивно пытается обновить таблицы.
Пожалуйста помоги
ORA-06512 — это стандартная ошибка PL / SQL, когда вызываемая процедура (или триггер) не работает.
Ora-04088 — это стандартная ошибка, которую Oracle выдает, когда выполняющийся триггер получает ошибку времени выполнения.
Основная причина — ошибка ORA-20001. Это диапазон исключений, определяемых пользователем, и вот! ваш триггер определяет один:
PRAGMA EXCEPTION_INIT( user_xcep, -20001 );
Триггер вызывает это исключение здесь:
IF :OLD.panchayat_id != :NEW.panchayat_id
THEN
RAISE user_xcep;
END IF;
Похоже, ваш оператор обновления пытается изменить значение panchayat_id
. Лучшим решением было бы удалить этот столбец из предложения SET:
SET name = ? ,panchayat_id = ?
Глядя на свой SpringBatch (или любой другой) код, возможно, вам стоит изменить проверку валидации. Возможно что-то вроде этого:
IF :OLD.panchayat_id is null then
null; -- allow update
ELSIF :NEW.panchayat_id is null then
:NEW.panchayat_id := :OLD.panchayat_id; -- don't wipe existing value
ELSIF :OLD.panchayat_id != :NEW.panchayat_id
THEN
RAISE user_xcep;
END IF;
3
APC
11 Окт 2018 в 09:46
Привет, посетитель сайта ZametkiNaPolyah.ru! Продолжаем изучать базы данных и наше знакомство с библиотекой SQLite3. Тем в рубрике SQLite осталось не так уж и много. Вернее про SQLite можно писать очень много, долго и упорно, и всё равно часть вопросов останется не освещенной и освещенной не в полной мере. Под словосочетанием «тем осталось немного» я понимаю следующее: мы практически закончили изучать реализацию SQL в библиотеки SQLite. Задачу, которую я сам перед собой поставил, можно озвучить следующим образом: дать начинающему разработчику максимально понятное и подробное представление о языке SQL, а в качестве примера используется библиотека SQLite. В данной теме мы поговорим о том, что собой представляют триггеры в SQL на примере базы данных под управлением SQLite. Тему триггеров я не стал делить на части, поэтому запись получилось довольно объемной(более 4300 слов, поэтому пользуйтесь постраничной навигацией).
Триггеры в SQL на примере базы данных SQLite
Отсутствие деления темы SQL триггеров на части не вызвано желанием написать огромный текст, просто все разделы данной записи очень тесно связаны между собой и я не захотел разбивать эту связь, делая деление на части. Итак, поехали! По традиции небольшая аннотация к записи:
- Сначала мы поговорим о назначении триггеров в SQL и реляционных базах данных, попутно рассмотрев синтаксис триггеров в SQLite.
- Затем мы поговорим о том, как срабатывают триггеры в базах данных: до возникновения события (триггер BEFORE) и после возникновения события (триггер AFTER) и параллельно разберемся в чем между ними разница.
- Далее мы опишем триггеры по событиям, на которые они срабатывают. Событий у нас всего три, так как триггеры в SQLite срабатывают только на операции, которые тем или иным образом изменяют данные в таблицы: UPDATE, INSERT, DELETE.
- Далее мы рассмотрим, как составить уточняющее выражение WHEN для триггера.
- Рассмотрим особенности INSTEAD OF триггера, который позволяет реализовать команды манипуляции данными для представлений, отметим, что в SQLite представления нельзя редактировать, об этом мы поговорим в следующей теме.
- Также мы поговорим про устранение конфликтов и обеспечение целостности данных при помощи триггеров и специальной функции RAISE.
- И в завершении публикации вы узнаете о том, как получить информацию о триггерах/списков триггеров в базах данных SQLite3.Рассмотрим явное и неявное удаление триггеров из базы данных SQLite. И разберемся с некоторыми особенностями работы временных триггеров в SQLite.
Что такое триггер в контексте SQL? Использование триггеров в базах данных SQLite
Содержание статьи:
- Что такое триггер в контексте SQL? Использование триггеров в базах данных SQLite
- SQL синтаксис триггеров в базах данных SQLite
- SQL событие BEFORE: выполнение триггера перед запросом
- SQL событие AFTER: выполнение триггера после запроса
- INSERT триггеры и DELETE триггеры. Триггеры добавления и удаления данных
- UPDATE триггеры: AFTER UPDATE и BEFORE UPDATE триггер. Триггеры модификации.
- Условия срабатывания триггера WHEN. Уточняющие выражения
- Некоторые особенности триггеров в базах данных SQLite
- Изменение данных VIEW в SQLite. Редактирование VIEW при помощи INSTEAD OF триггера в SQLite
- Функция RAISE () и SQL триггеры в библиотеки SQLite3. Устранение конфликтов в базе данных при помощи триггеров
- Временные триггеры в базах данных SQLite3
- Получит информацию о триггерах в базе данных SQLite
- Удаление триггеров из базы данных SQLite
Триггер – это особая разновидность хранимых процедур в базе данных. Особенность триггеров заключается в том, что SQL код, написанные в теле триггера, будет исполнен после того, как в базе данных произойдет какое-либо событие. События в базах данных происходят в результате выполнения DML команд или команд манипуляции данными. Если вы помните, то к командам манипуляции данными относятся: UPDATE, INSERT, DELETE и SELECT.
Команду SELECT мы не берем в расчет из-за того, что она никак не изменяет данные в базе данных, а лишь делает выборку данных. Основное назначение триггеров заключается в обеспечение целостности данных в базе данных, еще при помощи триггеров в SQL можно реализовать довольно-таки сложную бизнес-логику.
SQL код, написанный в теле триггера, будет выполнен автоматически, как только в базе данных произойдет одно из трех, указанных выше событий. Также мы можем задать самостоятельно события, по которым триггер будет срабатывать, а также SQL таблицу, для которой триггер будет срабатывать.
Для любой СУБД триггер – это в первую очередь объект базы данных, поэтому имя триггера должно быть уникальным во всей базе данных, SQLite в этом плане не исключение. У триггеров в SQL есть момент запуска. Момент запуска триггера можно разделить на два вида: BEFORE и AFTER. Момент запуска триггера AFTER говорит о том, что триггер будет запущен после выполнения какого-либо события в базе данных. Соответственно, момент запуска триггера BEFORE говорит о том, что триггер будет запущен до выполнения события в базе данных.
Мы еще поговорим про представления или VIEW в SQL, и вы узнаете, что SQLite позволяет только читать данные из VIEW, в отличии, скажем, от MySQL или Oracle. Триггеры могут быть назначены для представлений с целью расширить набор операций манипуляции данными того или иного представления. Такой вид триггеров получил название INSTEAD OF триггер.
Итак, триггеры можно разделить на три вида по их применению:
- триггер BEFORE, который срабатывает до выполнения какого-либо события в базе данных;
- триггер AFTER, который срабатывает после выполнения события в базе данных;
- INSTEAD OF триггер, который используется для манипуляции данными представлений.
Так же мы можем разделить триггеры по типам SQL команд:
- DELETE триггер. Триггер DELETE запускается при попытке удаления данных/строк из таблицы базы данных;
- UPDATE триггер. Триггер UPDATE будет запущен при попытке обновления/модификации данных в таблице базы данных;
- INSERT триггер. Триггер INSERT будет запущен в том случае, если вы попытаетесь вставить/добавить строку в таблицу базы данных.
В некоторых СУБД триггер – это довольно мощное и полезное явление. Будьте аккуратны, используя триггеры, не используйте триггеры в рабочих базах данных. Перед тем, как реализовать триггер, создайте тестовую базу данных и посмотрите, что в итоге получится. Неправильный составленный триггер может навредить вашему проекту, повредив часть данных или удалив данные из базы данных.
Давайте перечислим самые распространенные функции триггеров:
- Функция журнализации. Часто при помощи триггеров разработчики создают таблицы-журналы, в которых фиксируются различные изменения в базе данных. Обычно журналы создаются для фиксации изменений, которые вносят различные пользователи базы данных, таким образом можно отследить какой пользователь внес то или иное изменение в ту или иную таблицу базы данных.
- Функция согласования данных. Мы уже упоминали, что триггеры используются для обеспечения целостности данных в базе данных. Мы можем связать триггер с той или иной SQL командой, таким образом, чтобы триггер проверял связанные таблицы на согласованность данных, тем самым мы обезопасим данные.
- Функция очистки данных. Данная функция является подмножество функции из второго пункта. Например, вы выполняете каскадное удаление данных, в этом случае данные удаляются из таблиц, связанных ограничением внешнего ключа, но что если данные об одном объекте хранятся в несвязанных таблицах? В этом случае нас спасают триггеры. То же самое можно сказать и про операции каскадной модификации данных.
- Другие функции триггеров. К сожалению, в SQLite3 нет хранимых процедур за исключением триггеров. В тех СУБД, у которых реализованы хранимые процедуры, мы можем создавать собственные процедуры в теле триггера, которые могут выполнять операции, не связанные с изменением данных.
Давайте приступим к рассмотрению триггеров на примере библиотеки SQLite.
SQL синтаксис триггеров в базах данных SQLite
Здесь мы коротко рассмотрим SQL синтаксис триггеров, реализованный в реляционных базах данных под управлением библиотеки SQLite3. Ранее мы уже говорили о том, как создать триггер, когда разбирались с командой CREATE в SQLite (у нас был раздел CREATE TRIGGER) и мы рассматривали, как удалить триггер, когда разбирались с особенностями команды DROP в SQLite3 (раздел DROP TRIGGER). Давайте повторим и дополним уже имеющуюся информацию о триггерах. Общий SQL синтаксис создания триггеров в SQLite вы можете увидеть на рисунке ниже.
Общий синтаксис создания триггера в базе данных под управлением SQLite3
Мы видим, что операция по созданию триггера начинается с команды CREATE, как и операция создания таблицы в базе данных, это обусловлено тем, что триггер, как и таблица, является объектом базы данных.
Далее идет модификатор TEMP или TEMPORARY, этот модификатор необязательный и его можно опускать. Временный триггер будет доступен только для того пользователя, который его создали, а существовать временный триггер будет до тех пор, пока пользователь не разорвет соединение или же пока не удалит его.
Далее мы указываем, что хотим создать триггер при помощи ключевого слова TRIGGER. Мы можем воспользоваться оператором EXISTS, чтобы проверить существует ли триггер в базе данных, перед тем как его создать. Данная проверка не является обязательно, но если вы попытаетесь создать триггер, который уже существует в базе данных, то произойдет ошибка, а программный код, отправлявший такой запрос, может быть остановлен.
После ключевого слова TRIGGER мы указываем его имя, имя триггера должно быть уникальным во всей базе данных. Так же мы можем использовать квалификатор, чтобы указать полное имя триггера, состоящее из имени базы данных, в которой будет создан триггер и непосредственно имени триггера.
Далее мы указываем как мы хотим, чтобы триггер работал: для VIEW – INSTEAD OF, перед выполнением SQL команды – BEFORE, после выполнения SQL операции – AFTER. После чего мы связываем триггер с той или иной командой. Обратите внимание: для всех трех команд манипуляции данными обязательным является указание таблицы или представления, для которых триггер создается, а вот для команды UPDATE можно указать еще и столбец, который будет отслеживать триггер.
Обратите внимание: мы можем создавать строковые триггеры при помощи конструкции FOR EACH ROW. Обычно триггеры создаются для какой-нибудь команды и, соответственно, выполняются по событию DELETE, UPDATE или INSERT, но мы можем сделать так, чтобы код триггера вызывался после изменения каждой строки таблицы при помощи конструкции FOR EACH ROW.
Так же стоит отметить, что выше мы говорили не совсем правду в контексте SQLite3. Многие СУБД поддерживают две разновидности триггеров: табличные и строчные. Строчные триггеры создаются при помощи конструкции FOR EACH ROW, но в SQLite нет табличных триггеров, поэтому даже если вы не укажите FOR EACH ROW явно, SQLite будет считать триггер строчным.
Также вы можете использовать уточняющую фразу WHEN, с которой мы разберемся на примере ниже. После того, как вы описали триггер, вы можете задать SQL команды, которые будут выполняться по тому или иному событию, другими словами – создать тело триггера. В теле триггера, создаваемого в базе данных SQLite, можно использовать четыре команды манипуляции данными: INSERT, UPDATE, SELECT, DELETE. Команды определения данных, команды определения доступа к данным и команды управления транзакциями в теле триггера SQLite не предусмотрены. Но нам стоит заметить,что триггеры, выполняя команды и отлавливая события сами работают так, как будто это транзакция.
Обратим внимание на то, что перечисленные команды в теле триггера поддерживают свой практически полный синтаксис. Например, вы можете составить сколь угодно сложный SQL запрос SELECT, в котором будете объединять таблицы или объединять результаты запросов. Чтобы сообщить SQLite, что тело триггера закончилось, используйте ключевое слово END.
Итак, мы разобрались с SQL синтаксисом создания триггеров, давайте посмотрим на SQL синтаксис удаления триггеров. SQL синтаксис удаления триггеров, реализованный в SQLite3, представлен на рисунке ниже.
Синтаксис удаления триггеров из базы данных SQLite
Для удаления триггера, как и для удаления таблицы из базы данных, используйте команду DROP. Далее идет ключевая фраза TRIGGER, которая сообщает SQLite о том, что вы хотите удалить триггер из базы данных, после чего вы можете сделать проверку IF EXISTS, о которой мы не раз уже говорили. И в конце указываете имя триггера или квалификатор. Как видите, удалить триггер намного проще, чем его создать.
Давайте перейдем к примерам использования триггеров в базах данных под управлением SQLite.
SQL событие BEFORE: выполнение триггера перед запросом
Итак, не забываем, что триггер создается для какой-либо конкретной таблицы и отслеживает события, происходящие с таблицей, для которой он создан. В SQLite нет табличных триггеров, а есть только триггеры строчные, то есть FOR EACH ROW триггеры, которые срабатывают при изменении каждой строки в таблице.
Давайте напишем триггер, который будет срабатывать при вставке данных в базу данных, до того, как будет выполнена операция вставки. Но сначала создадим две таблицы, в первой мы будем хранить информацию о покупателе, во второй дату его посещения:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
CREATE TABLE users( id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER NOT NULL, address TEXT NOT NULL, mydate TEXT NOT NULL ); CREATE TABLE user_log ( Id_u INTEGER NOT NULL, u_date TEXT NOT NULL ); |
Таблицы довольно простые, заметьте, вторую таблицу мы не связывали с первой при помощи ограничения внешнего ключа FOREIGN KEY, так как для наполнения второй таблицы мы будем использовать триггер. Так же для таблиц мы задали ограничения уровня столбца и ограничения уровня таблицы. Первичный ключ или PRIMARY KEY – это не только ограничение, но еще и индекс таблицы в базе данных.
Давайте напишем триггер, который будет срабатывать перед тем, как SQLite выполнит запрос:
CREATE TRIGGER my_u_log BEFORE INSERT ON users BEGIN INSERT INTO user_log(id_u, u_date) VALUES (NEW.id, datetime(‘now’)); END; |
Этот триггер срабатает перед тем, как новая строка будет добавлена в таблицу users. Давайте в этом убедимся:
INSERT INTO users(name, age, address, mydate) VALUES (‘Пупкин’, 27, ‘Адрес’, datetime(‘now’)); SELECT * FROM users; id name age address mydate 1 Пупкин 27 Адрес 2016—07—15 06:58:36 SELECT * FROM user_log; Id_u u_date —1 2016—07—15 06:58:36 |
Мы видим, что данные в таблицу user_log были добавлены автоматически. К сожалению, поле date в данном случае не показывает, что вставка данных в таблицу user_log произошла до того, как были вставлены данные в таблицу users. Но этот факт мы можем заметить по значению столбца id_u, которое равно -1, так как SQLite3 просто не знает: какое значение будет в столбце id таблицы users.
SQL событие AFTER: выполнение триггера после запроса
Давайте теперь изменим наш пример, таблица останется той же, но мы изменим код триггера, только одну его часть: поменяем BEFORE на AFTER, чтобы посмотреть, как сработает триггер после выполнения запроса:
CREATE TRIGGER my_u_log AFTER INSERT ON users BEGIN INSERT INTO user_log(id_u, u_date) VALUES (NEW.id, datetime(‘now’)); END; |
Мы создали триггер (не забудьте удалить все объекты, чтобы пример работал корректно), который будет добавлять строки в таблицу user_log после того, как выполнится запрос INSERT, об этом нам говорит ключевое слово AFTER, давайте в этом убедимся:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
INSERT INTO users(name, age, address, mydate) VALUES (‘Пупкин’, 27, ‘Адрес’, datetime(‘now’)); INSERT INTO users(name, age, address, mydate) VALUES (‘Сумкин’, 17, ‘Адрес2’, datetime(‘now’)); SELECT * FROM users; id name age address mydate 1 Пупкин 27 Адрес 2016—07—15 07:08:46 2 Сумкин 17 Адрес2 2016—07—15 07:08:46 SELECT * FROM user_log; Id_u u_date 1 2016—07—15 07:08:46 2 2016—07—15 07:08:46 |
Теперь идентификаторы записываются корректно во вторую таблицу. Обратите внимание на модификатор NEW. Модификатор NEW – это ключевое слово, которое используется в теле триггера для того, чтобы сказать СУБД о том, что нужно брать новые значения (значение, которое мы добавляем в таблицу или модифицированный вариант значения). Надеюсь, что вы разобрались в разнице между BEFORE и AFTER.
INSERT триггеры и DELETE триггеры. Триггеры добавления и удаления данных
Ранее мы уже рассмотрели триггеры AFTER INSERT и BEFORE INSERT, поэтому мы не будем уделять здесь им особого внимания и сразу поговорим про триггеры BEFORE DELETE и AFTER DELETE. Таблица users у нас останется прежней, а вот структуру таблицы user_log мы немного изменим:
CREATE TABLE user_log ( Id_u INTEGER NOT NULL, u_date TEXT NOT NULL, operation TEXT NOT NULL ); |
Мы добавили столбец operation, в котором будем хранить информацию о том, что мы сделали с пользователем: удалили или добавили. Давайте напишем триггер AFTER DELETE, который у нас будет срабатывать по событию удаления строки из таблицы users:
CREATE TRIGGER after_delete AFTER DELETE ON users BEGIN INSERT INTO user_log(id_u, u_date, operation) VALUES (OLD.id, datetime(‘now’), ‘del’); END; |
А теперь давайте посмотрим на то, как будет работать триггер AFTER DELETE:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
INSERT INTO users(name, age, address, mydate) VALUES (‘Пупкин’, 27, ‘Адрес’, datetime(‘now’)); INSERT INTO users(name, age, address, mydate) VALUES (‘Сумкин’, 17, ‘Адрес2’, datetime(‘now’)); INSERT INTO users(name, age, address, mydate) VALUES (‘Иванов’, 37, ‘Адрес3’, datetime(‘now’)); INSERT INTO users(name, age, address, mydate) VALUES (‘Петров’, 47, ‘Адрес4’, datetime(‘now’)); INSERT INTO users(name, age, address, mydate) VALUES (‘Сидоров’, 57, ‘Адрес5’, datetime(‘now’)); INSERT INTO users(name, age, address, mydate) VALUES (‘Парамонов’, 7, ‘Адрес6’, datetime(‘now’)); DELETE FROM users WHERE id = 4; SELECT * FROM user_log; 1|2016—07—15 07:29:28|ins 2|2016—07—15 07:29:28|ins 3|2016—07—15 07:29:28|ins 4|2016—07—15 07:29:28|ins 5|2016—07—15 07:29:28|ins 6|2016—07—15 07:29:28|ins 4|2016—07—15 07:29:28|del SELECT * FROM users; id name age address mydate 1 Пупкин 27 Адрес 2016—07—15 07:29:28 2 Сумкин 17 Адрес2 2016—07—15 07:29:28 3 Иванов 37 Адрес3 2016—07—15 07:29:28 5 Сидоров 57 Адрес5 2016—07—15 07:29:28 6 Парамонов 7 Адрес6 2016—07—15 07:29:28 |
Чтобы получилась такие результаты, я немного изменил код первого триггера, добавив столбец operation. Но давайте обратим внимание на код триггера AFTER DELETE, в котором мы использовали модификатор OLD, модификатор OLD в SQL и SQLite используется в коде триггера для того, чтобы обратиться к старому значению или к значению, которое хранится в таблице (значение, которое будет изменено или модифицировано).
Теперь мы разобрались с модификаторами OLD и NEW:
- NEW позволяет обратиться к значению, которое указано в SQL запросе или же можно сказать, что это значение, которое мы хотим добавить или на которое хотим изменить.
- OLD позволяет обратиться к значению, которое хранится в таблице или же можно сказать, что это значение, которое мы хотим удалить или которое хотим изменить.
Также мы разобрались с тем, как работает триггер AFTER DELETE, думаю, вы без труда разберетесь с тем, как работает триггер BEFORE DELETE, просто заменив в примере триггера DELETE AFTER на BEFORE, поэтому демонстрировать триггер BEFORE DELETE мы здесь не будем и перейдем к другим примерам.
UPDATE триггеры: AFTER UPDATE и BEFORE UPDATE триггер. Триггеры модификации.
Теперь рассмотрим триггеры, срабатывающие при событии обновления данных: AFTER UPDATE триггер и BEFORE UPDATE триггер. Давайте напишем AFTER UPDATE триггер, который будет записывать в таблицу логов строки, хранящие информацию о модификации данных в таблице users, сделать это проще простого:
CREATE TRIGGER after_update AFTER UPDATE ON users BEGIN INSERT INTO user_log(id_u, u_date, operation) VALUES (OLD.id, datetime(‘now’), ‘upd’); END; |
Таким образом мы создали триггер, который сработает после выполнения команды UPDATE или AFTER UPDATE триггер, давайте в этом убедимся:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
UPDATE users SET name = ‘Марков’ WHERE id = 6; SELECT * FROM user_log; Id_u u_date operation 1 2016—07—15 07:29:28 ins ………………………………………………….. 6 2016—07—15 07:29:28 ins 4 2016—07—15 07:29:28 del 6 2016—07—15 08:12:44 upd SELECT * FROM users; id name age address mydate 1 Пупкин 27 Адрес 2016—07—15 07:29:28 …………………………………………………………………………….. 6 Марков 7 Адрес6 2016—07—15 07:29:28 |
Видим, что триггер сработал и в лог записалась информация о том, что для пользователя с id=6 вносились изменения. Думаю, вы легко разберетесь с тем, как сделать триггер BEFORE UPDATE по примеру AFTER UPDATE, чтобы вносимые изменения фиксировались до того, как они произойдут. Ничего сложно в триггере BEFORE UPDATE нет.
Обратите внимание: триггер модификации или UPDATE триггер может отслеживать изменения не только для всей таблицы, но и для какого-то конкретного столбца, чтобы указать столбец, который будет отслеживать триггер модификации, используйте следующий синтаксис:
CREATE TRIGGER trigg_name AFTER UPDATE OF (column1, column2) BEGIN — тело триггера END; |
Для такого триггера модификации вы можете использовать любое количество столбцов, имена которых указываются в круглых скобках, после ключевого слова OF идет тело триггеры модификации.
Условия срабатывания триггера WHEN. Уточняющие выражения
Библиотека SQLite, как и многие другие реляционные СУБД, позволяет использовать условие WHEN, которое позволяет задать область применения триггера при помощи SQL выражений. Синтаксис выражения в SQLite показан на рисунке ниже.
Синтаксис использования выражений в базах данных SQLite
Как видим, мы можем задавать очень сложные выражения для условия WHEN, когда мы создаем триггер. Любая СУБД вычисляет это выражение для строки таблицы, к которой привязан триггер? и в том случае, если WHEN вернет значение TRUE, триггер будет выполнен. SQLite не будет вычислять выражение WHEN, если не будет происходить событие, инициирующее выполнение триггера.
Давайте напишем триггер с условием WHEN, который будет срабатывать, как AFTER INSERT:
CREATE TRIGGER after_insert AFTER INSERT ON users WHEN (SELECT count(*) FROM user_log) > 21 BEGIN DELETE FROM user_log WHERE u_date = (SELECT min(u_date) FROM user_log); INSERT INTO user_log(id_u, u_date, operation) VALUES (NEW.id, datetime(‘now’), ‘ins’); END; |
Этот триггер делает очень простую вещь: он ограничивает количество записей в логе до двадцати одной. То есть в таблице user_log будет храниться информация не обо всех модификациях, а только о последних, понятно, что количество записей в таблице можно регулировать.
Попробуйте реализовать данный триггер, чтобы посмотреть, как работает условие WHEN. Демонстрировать его работу здесь мы не будем. Также обратите внимание на то, что теперь в теле триггера выполняется две операции: первая удаляет лишнюю строку из лог-таблицы, вторая добавляет новую строку в таблицу лога.
Некоторые особенности триггеров в базах данных SQLite
Ранее мы говорили, что в теле триггера можно использовать любую команду манипуляции данных с довольно полным синтаксисом. Но ключевая часть выражения здесь: довольно полный синтаксис. Всё дело в том, что синтаксис команд манипуляции данными в теле триггера SQLite поддерживается не полностью. Итак, ваш триггер не будет создан/не будет работать если:
- В теле триггера вы не можете использовать квалификаторы для обращения к таблицам базы данных. Можно использовать только имена таблиц.
- Когда в деле триггера вы выполняете операцию INSERT, то вы не можете добавлять значения DEFAULT. Добавляемые значения должны быть явно указаны.
- Нельзя использовать ключевые слова INDEX BY и NOT INDEXED в командах UPDATE и DELETE.
- Клаузулы LIMIT и ORDER BY нельзя использовать с командами UPDATE и DELETE в теле триггера.
Вот такие ограничения накладывает SQLite на команды SQL, которые мы можем использовать в теле триггера.
Изменение данных VIEW в SQLite. Редактирование VIEW при помощи INSTEAD OF триггера в SQLite
В SQLite нет возможности редактировать VIEW. Напомним, что VIEW в SQL – это хранимый запрос, который СУБД выполняет, когда мы обращаемся к представлениям. Но мы можем манипулировать данными VIEW, которые хранятся в представлениях (данное выражение не совсем корректно, так как данные в представлениях не хранятся, это всего лишь результирующая таблица запроса SELECT) при помощи INSTEAD OF триггера.
Стоит заметить, что функция манипулирования данными представлений – это основная функция триггера INSTEAD OF, но не единственная. Триггер INSTEAD OF работает очень интересно, он позволяет выполнять команды INSERT, DELETE и UPDATE над представлениями, но результаты изменений, внесенных триггером INSTEAD OF, никак не отражаются на таблице/таблицах, на основе которых создано VIEW.
Общий синтаксис триггера INSTEAD OF UPDATE для обновления данных представления можно записать как:
CREATE TRIGGER trigg_name INSTEAD OF UPDATE OF column_name ON view_name BEGIN — делаем команду UPDATE для таблицы, на основе которой создана VIEW END; |
Синтаксис триггера INSTEAD OF INSERT для добавления строк в представление выглядит так:
CREATE TRIGGER trigg_name INSTEAD OF INSERT ON view_name BEGIN — делаем команду INSERT для таблицы, на основе которой создана VIEW END; |
Синтаксис триггера INSTEAD OF DELETE для удаления строк из представления выглядит так:
CREATE TRIGGER trigg_name INSTEAD OF DELETE ON view_name BEGIN — делаем команду DELETE для таблицы, на основе которой создана VIEW END; |
Мы познакомились с основным способом применения триггера INSTEAD OF в SQL и базах данных SQLite. Более подробные примеры по редактированию VIEW при помощи INSTEAD OF триггера в SQLite3, вы найдете в следующей теме, в которой речь пойдет о представлениях.
Функция RAISE () и SQL триггеры в библиотеки SQLite3. Устранение конфликтов в базе данных при помощи триггеров
Мы очень много говорили про обеспечение целостности данных и когда рассматривали различные ограничения СУБД SQLite, и когда говорили про нормальные формы в базе данных. Напомню, что первая нормальная форма – самое незащищенное отношение в базе данных. К тому же, первая нормальная форма – это всегда избыточность данных, плюс ко всему – это всевозможные проблемы, которые называются аномалиями. Вторая нормальная форма помогает нам избавиться от избыточности данных.
А третья нормальная форма устраняет всевозможные аномалии и различные зависимости, это всё важно знать и понимать при проектировании базы данных, но, к сожалению, ни одна в мире СУБД не знает, например, что Хемингуэй написал «Старик и море», а Исаак Ньютон – это физик, поэтому когда мы наполняем наши таблицы данными, могут возникать не очевидные ошибки с точки зрения логики реляционных баз данных, от которых не сможет защитить ни одна нормальная форма.
Например, когда мы говорили про внешние ключи, мы реализовывали связь один ко многим между таблицами, для этого мы создавали результирующую таблицу и я демонстрировал пример неудачно добавления данных, при котором получалось, что Джек Лондон написал «Войну и мир». Такие ошибки могут происходить довольно часто и их можно назвать конфликтами в базе данных.
Конфликт в базе данных не стоит путать с аномалией, потому что с точки зрения логики СУБД ничего криминального не происходит, а вот с точки зрения человека, знающего предметную область происходит существенная ошибка, которая может ввести человека в ступор.
Избавиться от подобных конфликтов мы можем при помощи триггеров и функции RAISE (). В SQLite есть специальные ключевые слова ON CONFLICT, которые используются для исключения конфликтов, происходящих во время операций манипуляции данными. О устранении конфликтов мы поговорим более подробно отдельно. Сейчас сконцентрируемся на триггерах и функции RAISE (). Отметим, что RAISE () – это специальная функция, которая используется только вместе с триггерами, ее синтаксис вы найдете на рисунке ниже.
Функция RAISE может принимать два аргумента. Первый аргумент описывает действие при возникновении конфликта:
- Значение IGNORE. Говорит SQLite3 о том, чтобы она игнорировала строку породившую конфликт и продолжала выполнение последующих операций.
- Значение ROLLBACK. ROLLBACK говорит о том, что SQLite должна откатить все операции к исходному состоянию при возникновении конфликта. При этом пользователь может изменить и повторить запрос. Не путайте с командой ROLLBACK, которая откатывая транзакцию
- Значение ABORT. Данное значение похоже на ROLLBACK, но разница в том, что оно отменяет не все выполненные ранее SQL запросы, а только тот запрос, при котором возникла конфликтная ситуация.
- Значение FAIL. Данное значение говорит СУБД о том, что нужно прервать выполнение текущей операции и сохранить результаты успешных операций, при этом операции, следующие за конфликтной, выполнены не будут.
Вообще тело триггера выполняется как транзакция, мы даже используем похожие команды, например тело триггера начинается после ключевого слова BEGIN, а вот начало транзакции обозначается ключевой фразой BEGIN TRANSACTION. Подтверждение транзакции- COMMIT или END, а завершение тела триггера обозначается при помощи ключевого слова END. А функция RAISE дает возможность триггеру работать, как команда SAVEPOINT.
Второй аргумент, который принимает функция триггера RAISE – это пояснение к ошибке. Пояснение – это обычная строка, которую вы вводите с клавиатуры. По сути данное пояснение является сообщением об ошибке, которая произошла в результате операции манипуляции данными.
Давайте повторим пример связи многие ко многим:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
PRAGMA foreign_keys=on; CREATE TABLE books( Id INTEGER PRIMARY KEY, title TEXT NOT NULL, count_page INTEGER NOT NULL CHECK (count_page >0), price REAL CHECK (price >0) ); CREATE TABLE auth( id INTEGER PRIMARY KEY, name TEXT NOT NULL, age INTEGER CHECK (age >16) ); CREATE TABLE auth_book ( auth_id INTEGER NOT NULL, books_id INTEGER NOT NULL, FOREIGN KEY (auth_id) REFERENCES auth(id) FOREIGN KEY (books_id) REFERENCES books(id) ); — Сперва добавим несколько строк в таблицу книг INSERT INTO books (id, title, count_page, price) VALUES (1, ‘Белый клык’, 287, 300.00); INSERT INTO books (id, title, count_page, price) VALUES (2, ‘Война и мир’, 806, 780.00); INSERT INTO books (id, title, count_page, price) VALUES (3, ’12 стульев’, 516, 480.00); — Затем добавим несколько авторов INSERT INTO auth (id, name, age) VALUES (1, ‘Джек Лондон’, 40); INSERT INTO auth (id, name, age) VALUES (2, ‘Лев Толстой’, 82); INSERT INTO auth (id, name, age) VALUES (3, ‘Илья Ильф’, 39); INSERT INTO auth (id, name, age) VALUES (4, ‘Евгений Петров’, 38); |
Мы наполнили две таблицы, но не наполнили таблицу-справочник, которая реализует связь многие ко многим. Давайте напишем триггер, который будет проверять смысл значений перед их добавлением в таблицу-справочник.
Давайте сперва реализуем триггер с использованием функции RAISE (), который будет проверять данные перед их вставкой:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
CREATE TRIGGER books_result BEFORE INSERT ON auth_book BEGIN SELECT RAISE(FAIL, ‘Произошла ошибка, вы неправильно связали автора и книгу’) FROM auth_book WHERE (NEW.auth_id = 1 AND books_id = 2) OR (NEW.auth_id = 1 AND NEW.books_id = 3) OR (NEW.auth_id = 2 AND NEW.books_id = 3) OR (NEW.auth_id = 2 AND NEW.books_id = 3) OR (NEW.auth_id = 3 AND NEW.books_id = 2) OR (NEW.auth_id = 3 AND NEW.books_id = 1) OR (NEW.auth_id = 4 AND NEW.books_id = 2) OR (NEW.auth_id = 4 AND NEW.books_id = 1); END; |
Конечно, пример не самый эффективный и не самый жизненный, функция RAISE в триггере проверяет и сравнивает значения столбцов, при этом строк у нас не очень много в таблицах. В его защиту скажу, что он хорошо демонстрирует возможности функции RAISE по обеспечению целостности данных. Ссылку NEW мы использовали, потому что нам необходимо проверять данные, которые мы хотим добавить в таблицу.
А вся это красота будет работать благодаря тому, что команды внутри триггера выполняются так, как будто это транзакция. Давайте теперь попытаемся наполнить результирующую таблицу данными.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
INSERT INTO auth_book (auth_id, books_id) VALUES (1, 1); INSERT INTO auth_book (auth_id, books_id) VALUES (2, 2); INSERT INTO auth_book (auth_id, books_id) VALUES (3, 3); INSERT INTO auth_book (auth_id, books_id) VALUES (4, 3); INSERT INTO auth_book (auth_id, books_id) VALUES (1, 2); Error: Произошла ошибка, вы неправильно связали автора и книгу SELECT * FROM auth_book; auth_id books_id 1 1 2 2 3 3 4 3 |
Этот триггер срабатывает при каждой операции добавления строки в таблицу, а функция RAISE сравнивает добавляемое значение с исходным, которое мы задавали при помощи SQL операторов AND и OR в условии WHERE. Обратите внимание: и скобки, знак равно – это тоже SQL операторы.
Временные триггеры в базах данных SQLite3
Демонстрировать примеры создания временных триггеров в базах данных SQLite3 мы не будем. Скажем лишь о том, что временный триггер доступен только для создавшего его пользователя и существует временный триггер всё то время, пока пользователь не разорвет сессию, либо не удалит триггер.
Отметим, что временные триггеры могут быть созданы не только для временных таблиц, но и для обычных таблиц.
Получит информацию о триггерах в базе данных SQLite
Вы можете получить информацию о триггерах или посмотреть созданные триггеры в базе данных SQLite при помощи команды SELECT, следующее предложение покажет вам все триггеры в базе данных:
SELECT name FROM sqlite_master WHERE type = ‘trigger’; |
Если вы хотите получить список триггеров для таблицы в базе данных SQLite, то воспользуйтесь следующей командой:
SELECT name FROM sqlite_master WHERE type = ‘trigger’ AND tbl_name = ‘укажите_имя_таблицы’; |
Таким образом вы можете получить список триггеров во всей базе данных или для конкретной таблицы в базе данных SQLite. Обратит внимание: в других СУБД эти команды работать не будут.
Удаление триггеров из базы данных SQLite
Поговорим теперь про удаление триггеров из базы данных. Вспомним общий синтаксис удаления триггера из базы данных:
DROP TRIGGER IF EXISTS db_name.trigger_name; |
Таким образом мы можем явно удалить триггер из базы данных SQLite. Неявное удаление триггеров происходит при удалении из базы данных таблицы, к которой он привязан. Если вы привязали обычный триггер к временной таблице, то можете смело его считать временным, так как он будет существовать до первого разрыва соединения с базой данных, после чего триггер будет удален, так как таблица перестанет существовать.
Так как триггеры БД довольно обширная тема, продолжаем разбирать ее далее. Давайте рассмотрим вариант, когда триггер работает с активизирующей его таблицей. Если говорить о работе триггеров таблиц, то как оказывается, триггер может обращаться не ко всем таблицам и столбцам этих таблиц. Для того, чтобы определить к каким таблицам возможен доступ, необходимо понимать, что такое изменяющиеся и ограничивающие таблицы. Изменяющиеся таблица (mutating table) — это именно та таблица, которая в данный момент модифицируется оператором DML! Для триггера это та таблица, для которой он был создан! Так же и те таблицы, которые обновляются в результате реализации или действия ссылочной целостности. Таблицы выполняющие каскадные удаления — DELETE CASCADE, так же являются изменяющимися. Ограничивающая таблица (constraining table) — это таблица, информация которой может быть считана при реализации ограничений ссылочной целостности. Вот такие трудности могут быть на пути создания триггеров БД. Давайте рассмотрим пример «неверного» триггера БД. Запишем вот такой триггер для таблицы TSTTRIG:
CREATE OR REPLACE TRIGGER ERRTRIG AFTER INSERT ON TSTTRIG FOR EACH ROW BEGIN UPDATE TSTTRIG SET ROD = LOWER(ROD) WHERE ID = :old.ID; END ERRTRIG; /
Получаем:
SQL> CREATE OR REPLACE TRIGGER ERRTRIG 2 AFTER INSERT ON TSTTRIG 3 FOR EACH ROW 4 5 BEGIN 6 7 UPDATE TSTTRIG 8 SET ROD = LOWER(ROD) 9 WHERE ID = :old.ID; 10 11 END ERRTRIG; 12 / Триггер создан.
Здесь для простоты примера я произвожу модификацию столбца ROD после вставки строки. Пробуем добавить, запись в таблицу TSTTRIG:
INSERT INTO TSTTRIG (NM, ROD, INRW) VALUES ('BOB', 'DUMMY', TO_DATE('18-12-2004', 'DD-MM-YYYY')) /
Получаем:
SQL> INSERT INTO TSTTRIG (NM, ROD, INRW) 2 VALUES ('BOB', 'DUMMY', TO_DATE('18-12-2004', 'DD-MM-YYYY')) 3 / INSERT INTO TSTTRIG (NM, ROD, INRW) * ошибка в строке 1: ORA-04091: таблица MILLER.TSTTRIG изменяется, триггер/функция может не заметить это ORA-06512: на "MILLER.ERRTRIG", line 5 ORA-04088: ошибка во время выполнения триггера 'MILLER.ERRTRIG'
Как видно сразу три типа ошибки вызывает эта операция! Но основная из них это ORA-04091! То есть для триггера MILLER.TSTTRIG таблица TSTTRIG является изменяющейся и модифицировать ее с помощью операторов DML он не может! Можете удалить триггер MILLER.TSTTRIG, чтобы он нам не мешал. Как это делать я думаю вы знаете! Итак, подведем черту.
SQL — операторы в теле триггера не могут:
- Считывать или модифицировать информацию, любой таблицы изменяющейся в результате выполнения активизирующего оператора. В число таких таблиц входит и сама активизирующая таблица.
- Считывать или модифицировать информацию столбца первичного ключа, уникальных столбцов и столбцов внешних ключей таблицы, являющейся ограничивающей по отношению к изменяющейся таблице. (уфф .. запутанная формулировочка! Но верная!).
Заметим, что эти правила верны для строковых триггеров. Для операторных триггеров они применимы только в тех случаях, когда последние активизируются в результате выполнения операции каскадного удаления информации. Следует так же сказать, что оператор INSERT воздействующий только на одну строку (хм .. ну естественно он же ее добавляет — ведь так? ) для строковых триггеров BEFORE и AFTER работающих с этой строкой активизирующая таблица как ни странно не является изменяющейся! Это единственная ситуация когда строковый триггер может считывать или модифицировать информацию активизирующей таблицы. Но! Для таких операторов как:
INSERT INTO таблица SELECT ......
активизирующая таблица всегда является изменяющейся, даже если в подзапросе возвращается только одна строка! Вот так бывает много разных ситуаций, про которые не следует забывать, а просто применять их на практике при создании триггеров для таблиц. А, что если вам все же необходимо получать данные в триггере от самой инициирующей таблицы? Тогда можно поступить следующим образом. Создать два триггера, один операторный другой строчный и производить все необходимые действия с их помощью. Давайте попробуем это проиллюстрировать, но здесь вам нужно будет кое-что вспомнить, если забыли обращайтесь к прошлым шагам там все есть! Итак, начнем с написания такого пакета:
CREATE OR REPLACE PACKAGE TrigTest IS TYPE m_ID IS TABLE OF TSTTRIG.ID%TYPE INDEX BY BINARY_INTEGER; TYPE m_NM IS TABLE OF TSTTRIG.NM%TYPE INDEX BY BINARY_INTEGER; V_m_ID m_ID; V_m_NM m_NM; V_num BINARY_INTEGER := 0; END TrigTest; /
Получаем:
SQL> CREATE OR REPLACE PACKAGE TrigTest IS 2 3 TYPE m_ID IS TABLE OF TSTTRIG.ID%TYPE 4 INDEX BY BINARY_INTEGER; 5 6 TYPE m_NM IS TABLE OF TSTTRIG.NM%TYPE 7 INDEX BY BINARY_INTEGER; 8 9 V_m_ID m_ID; 10 V_m_NM m_NM; 11 V_num BINARY_INTEGER := 0; 12 13 END TrigTest; 14 / Пакет создан.
Здесь мы создали две таблицы V_m_ID и V_m_NM и переменную V_num для обращения к записям таблиц. Все достаточно не сложно! Теперь создадим такой строчный триггер:
CREATE OR REPLACE TRIGGER FIXTRG BEFORE INSERT OR UPDATE OF NM ON TSTTRIG FOR EACH ROW BEGIN TrigTest.V_num := TrigTest.V_num +1; TrigTest.V_m_ID(TrigTest.V_num) := :new.ID; TrigTest.V_m_NM(TrigTest.V_num) := :new.NM; END FIXTRG; /
Получаем:
SQL> CREATE OR REPLACE TRIGGER FIXTRG 2 BEFORE INSERT OR UPDATE OF NM ON TSTTRIG 3 FOR EACH ROW 4 5 BEGIN 6 7 TrigTest.V_num := TrigTest.V_num +1; 8 TrigTest.V_m_ID(TrigTest.V_num) := :new.ID; 9 TrigTest.V_m_NM(TrigTest.V_num) := :new.NM; 10 11 END FIXTRG; 12 / Триггер создан.
Этот триггер фиксирует значения двух полей таблицы TSTTRIG в наших пакетных таблицах V_m_ID и V_m_NM. Далее создаем вот такой операторный триггер:
CREATE OR REPLACE TRIGGER OPERTRG BEFORE INSERT OR UPDATE OF NM ON TSTTRIG DECLARE V_CNT NUMBER; V_COUNT CONSTANT NUMBER := 10; BEGIN SELECT COUNT(*) INTO V_CNT FROM TSTTRIG; IF( TrigTest.V_num = V_COUNT ) THEN TrigTest.V_m_ID(TrigTest.V_num) := V_CNT; TrigTest.V_m_NM(TrigTest.V_num) := 'STOP'; TrigTest.V_num := 0; END IF; END OPERTRG; /
Получаем:
SQL> CREATE OR REPLACE TRIGGER OPERTRG 2 BEFORE INSERT OR UPDATE OF NM ON TSTTRIG 3 4 DECLARE 5 6 V_CNT NUMBER; 7 V_COUNT CONSTANT NUMBER := 10; 8 9 BEGIN 10 11 SELECT COUNT(*) INTO V_CNT 12 FROM TSTTRIG; 13 14 IF( TrigTest.V_num = V_COUNT ) THEN 15 TrigTest.V_m_ID(TrigTest.V_num) := V_CNT; 16 TrigTest.V_m_NM(TrigTest.V_num) := 'STOP'; 17 TrigTest.V_num := 0; 18 END IF; 19 20 21 END OPERTRG; 22 / Триггер создан.
Здесь саму идею я высосал из пальца, собственно триггер ничего путного не производит, а только фиксирует количество записей в таблице TSTTRIG в пакетных таблицах (запутался совсем в таблицах)! Но здесь явно видно, что триггер OPERTRG делает запрос к инициирующей таблице и это ему сходит с рук! Что собственно и требовалось показать! Сами можете придумать что-то более путное, но моя задача состоит в том, чтобы Вы поняли как это все происходит или запутались окончательно! Но, думаю сможете все это разобрать! Теперь давайте попробуем поизменять поля таблицы TSTTRIG два три раза:
UPDATE TSTTRIG SET NM = 'ALF' WHERE NM = 'BOB' / UPDATE TSTTRIG SET NM = 'BOB' WHERE NM = 'ALF' / UPDATE TSTTRIG SET NM = 'BUBER' WHERE NM = 'SCOTT' / UPDATE TSTTRIG SET NM = 'SCOTT' WHERE NM = 'BUBER' / COMMIT /
Получаем:
SQL> UPDATE TSTTRIG 2 SET NM = 'ALF' 3 WHERE NM = 'BOB' 4 / 1 строка обновлена. SQL> UPDATE TSTTRIG 2 SET NM = 'BOB' 3 WHERE NM = 'ALF' 4 / 1 строка обновлена. SQL> UPDATE TSTTRIG 2 SET NM = 'BUBER' 3 WHERE NM = 'SCOTT' 4 / 1 строка обновлена. SQL> UPDATE TSTTRIG 2 SET NM = 'SCOTT' 3 WHERE NM = 'BUBER' 4 / 1 строка обновлена. SQL> COMMIT 2 / Фиксация обновлений завершена.
А затем напишем вот такой неименованный блок и посмотрим, что он нам выдаст по части содержимого пакетных табличек:
SET SERVEROUTPUT ON DECLARE BEGIN FOR V_LOOP IN 1..TrigTest.V_m_NM.COUNT LOOP DBMS_OUTPUT.enable; DBMS_OUTPUT.put_line(TO_CHAR(TrigTest.V_m_ID(V_LOOP))); DBMS_OUTPUT.put_line(TrigTest.V_m_NM(V_LOOP)); END LOOP; END; /
Получаем:
SQL> SET SERVEROUTPUT ON SQL> DECLARE 2 3 BEGIN 4 5 FOR V_LOOP IN 1..TrigTest.V_m_NM.COUNT LOOP 6 7 DBMS_OUTPUT.enable; 8 DBMS_OUTPUT.put_line(TO_CHAR(TrigTest.V_m_ID(V_LOOP))); 9 DBMS_OUTPUT.put_line(TrigTest.V_m_NM(V_LOOP)); 10 11 END LOOP; 12 13 END; 14 / 8041 ALF 8041 BOB 7372 BUBER 7372 SCOTT 8041 ALF 8041 BOB 7372 BUBER 7372 SCOTT Процедура PL/SQL успешно завершена.
Вот здесь ясно видно, что вся работа триггеров прошла успешно и больше здесь комментировать нечего! Думаю, теперь вам ясно, как избежать ошибок при написании триггеров при использовании изменяющихся таблиц и основные правила работы с ними. Если что-то не совсем ясно, пишите, буду рад ответить!
I created a trigger as below:
CREATE OR REPLACE TRIGGER trigger_test
AFTER INSERT ON trigger_1
FOR EACH ROW
DECLARE
t_identifier VARCHAR2(10);
t_name VARCHAR2(20);
BEGIN
t_identifier := (:NEW.IDENTIFIER);
t_name := (:NEW.NAME);
INSERT INTO trigger_2(IDENTIFIER,NAME)VALUES(t_identifier,t_name);
COMMIT;
END;
I am trying to insert a row in trigger_1
INSERT INTO trigger_1(IDENTIFIER,NAME)
VALUES('1234567','Vijay');
It is giving me the errors:
ORA-04092: cannot COMMIT in a trigger
ORA-06512: at "LVSDBO46.TRIGGER_TEST", line 8
ORA-04088: error during execution of trigger 'LVSDBO46.TRIGGER_TEST'
Could nybody please help?
I am facing following problem. I created a table with following sql in Oracle 11g release 2:
create table loc_aud
(
username varchar2(20),
audittime date,
IP VARCHAR2(30),
locno number(4),
old_city number(4),
new_city number(4)
);
This table is in sys schema. Then I created a trigger for value base auditing using following command in sys schema
CREATE OR REPLACE TRIGGER location_audit
AFTER UPDATE OF city
ON hr.locations
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
IF :old.city != :new.city THEN
INSERT INTO loc_aud
VALUES (user, sysdate, UTL_INADDR.get_host_address,
:new.location_id,:old.city,:new.city);
END IF;
END;
After that I connected with hr schema and tried to update the city
column with following command:
update locations set city = 'Dhaka' where location_id = 2100;
But it is giving me following errors
update locations set city = 'Dubai' where location_id = 2100
*
ERROR at line 1:
ORA-01722: invalid number
ORA-06512: at "SYS.LOCATION_AUDIT", line 3
ORA-04088: error during execution of trigger 'SYS.LOCATION_AUDIT'
What am I doing wrong?
May 1, 2021
I got ” ORA-04088: error during execution of trigger ‘string.string’” error in Oracle database.
ORA-04088: error during execution of trigger ‘string.string’
Details of error are as follows.
ORA-04088 error during execution of trigger 'string.string' Cause: A runtime error occurred during execution of a trigger. Action: Check the triggers which were involved in the operation.
error during execution of trigger ‘string.string’
This ORA-04088 errors are related with the runtime error occurred during execution of a trigger.
when I check related table triggers and constraints, problem is occured because of trigger.
I have disabled triggers with following command.
alter trigger TRIGGER_NAME disable;
Or Check and fix the triggers which were involved in the operation.
Do you want to learn Oracle Database for Beginners, then read the following articles.
Oracle Tutorial | Oracle Database Tutorials for Beginners ( Junior Oracle DBA )
1,370 views last month, 1 views today
About Mehmet Salih Deveci
I am Founder of SysDBASoft IT and IT Tutorial and Certified Expert about Oracle & SQL Server database, Goldengate, Exadata Machine, Oracle Database Appliance administrator with 10+years experience.I have OCA, OCP, OCE RAC Expert Certificates I have worked 100+ Banking, Insurance, Finance, Telco and etc. clients as a Consultant, Insource or Outsource.I have done 200+ Operations in this clients such as Exadata Installation & PoC & Migration & Upgrade, Oracle & SQL Server Database Upgrade, Oracle RAC Installation, SQL Server AlwaysOn Installation, Database Migration, Disaster Recovery, Backup Restore, Performance Tuning, Periodic Healthchecks.I have done 2000+ Table replication with Goldengate or SQL Server Replication tool for DWH Databases in many clients.If you need Oracle DBA, SQL Server DBA, APPS DBA, Exadata, Goldengate, EBS Consultancy and Training you can send my email adress [email protected].- -Oracle DBA, SQL Server DBA, APPS DBA, Exadata, Goldengate, EBS ve linux Danışmanlık ve Eğitim için [email protected] a mail atabilirsiniz.
I am facing following problem. I created a table with following sql in Oracle 11g release 2:
create table loc_aud
(
username varchar2(20),
audittime date,
IP VARCHAR2(30),
locno number(4),
old_city number(4),
new_city number(4)
);
This table is in sys schema. Then I created a trigger for value base auditing using following command in sys schema
CREATE OR REPLACE TRIGGER location_audit
AFTER UPDATE OF city
ON hr.locations
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
IF :old.city != :new.city THEN
INSERT INTO loc_aud
VALUES (user, sysdate, UTL_INADDR.get_host_address,
:new.location_id,:old.city,:new.city);
END IF;
END;
After that I connected with hr schema and tried to update the city
column with following command:
update locations set city = 'Dhaka' where location_id = 2100;
But it is giving me following errors
update locations set city = 'Dubai' where location_id = 2100
*
ERROR at line 1:
ORA-01722: invalid number
ORA-06512: at "SYS.LOCATION_AUDIT", line 3
ORA-04088: error during execution of trigger 'SYS.LOCATION_AUDIT'
What am I doing wrong?
May 1, 2021
I got ” ORA-04088: error during execution of trigger ‘string.string’” error in Oracle database.
ORA-04088: error during execution of trigger ‘string.string’
Details of error are as follows.
ORA-04088 error during execution of trigger 'string.string' Cause: A runtime error occurred during execution of a trigger. Action: Check the triggers which were involved in the operation.
error during execution of trigger ‘string.string’
This ORA-04088 errors are related with the runtime error occurred during execution of a trigger.
when I check related table triggers and constraints, problem is occured because of trigger.
I have disabled triggers with following command.
alter trigger TRIGGER_NAME disable;
Or Check and fix the triggers which were involved in the operation.
Do you want to learn Oracle Database for Beginners, then read the following articles.
Oracle Tutorial | Oracle Database Tutorials for Beginners ( Junior Oracle DBA )
1,214 views last month, 1 views today
About Mehmet Salih Deveci
I am Founder of SysDBASoft IT and IT Tutorial and Certified Expert about Oracle & SQL Server database, Goldengate, Exadata Machine, Oracle Database Appliance administrator with 10+years experience.I have OCA, OCP, OCE RAC Expert Certificates I have worked 100+ Banking, Insurance, Finance, Telco and etc. clients as a Consultant, Insource or Outsource.I have done 200+ Operations in this clients such as Exadata Installation & PoC & Migration & Upgrade, Oracle & SQL Server Database Upgrade, Oracle RAC Installation, SQL Server AlwaysOn Installation, Database Migration, Disaster Recovery, Backup Restore, Performance Tuning, Periodic Healthchecks.I have done 2000+ Table replication with Goldengate or SQL Server Replication tool for DWH Databases in many clients.If you need Oracle DBA, SQL Server DBA, APPS DBA, Exadata, Goldengate, EBS Consultancy and Training you can send my email adress [email protected].- -Oracle DBA, SQL Server DBA, APPS DBA, Exadata, Goldengate, EBS ve linux Danışmanlık ve Eğitim için [email protected] a mail atabilirsiniz.
I am facing following problem. I created a table with following sql in Oracle 11g release 2:
create table loc_aud
(
username varchar2(20),
audittime date,
IP VARCHAR2(30),
locno number(4),
old_city number(4),
new_city number(4)
);
This table is in sys schema. Then I created a trigger for value base auditing using following command in sys schema
CREATE OR REPLACE TRIGGER location_audit
AFTER UPDATE OF city
ON hr.locations
REFERENCING NEW AS NEW OLD AS OLD
FOR EACH ROW
BEGIN
IF :old.city != :new.city THEN
INSERT INTO loc_aud
VALUES (user, sysdate, UTL_INADDR.get_host_address,
:new.location_id,:old.city,:new.city);
END IF;
END;
After that I connected with hr schema and tried to update the city
column with following command:
update locations set city = 'Dhaka' where location_id = 2100;
But it is giving me following errors
update locations set city = 'Dubai' where location_id = 2100
*
ERROR at line 1:
ORA-01722: invalid number
ORA-06512: at "SYS.LOCATION_AUDIT", line 3
ORA-04088: error during execution of trigger 'SYS.LOCATION_AUDIT'
What am I doing wrong?
May 1, 2021
I got ” ORA-04088: error during execution of trigger ‘string.string’” error in Oracle database.
ORA-04088: error during execution of trigger ‘string.string’
Details of error are as follows.
ORA-04088 error during execution of trigger 'string.string' Cause: A runtime error occurred during execution of a trigger. Action: Check the triggers which were involved in the operation.
error during execution of trigger ‘string.string’
This ORA-04088 errors are related with the runtime error occurred during execution of a trigger.
when I check related table triggers and constraints, problem is occured because of trigger.
I have disabled triggers with following command.
alter trigger TRIGGER_NAME disable;
Or Check and fix the triggers which were involved in the operation.
Do you want to learn Oracle Database for Beginners, then read the following articles.
Oracle Tutorial | Oracle Database Tutorials for Beginners ( Junior Oracle DBA )
1,214 views last month, 1 views today
About Mehmet Salih Deveci
I am Founder of SysDBASoft IT and IT Tutorial and Certified Expert about Oracle & SQL Server database, Goldengate, Exadata Machine, Oracle Database Appliance administrator with 10+years experience.I have OCA, OCP, OCE RAC Expert Certificates I have worked 100+ Banking, Insurance, Finance, Telco and etc. clients as a Consultant, Insource or Outsource.I have done 200+ Operations in this clients such as Exadata Installation & PoC & Migration & Upgrade, Oracle & SQL Server Database Upgrade, Oracle RAC Installation, SQL Server AlwaysOn Installation, Database Migration, Disaster Recovery, Backup Restore, Performance Tuning, Periodic Healthchecks.I have done 2000+ Table replication with Goldengate or SQL Server Replication tool for DWH Databases in many clients.If you need Oracle DBA, SQL Server DBA, APPS DBA, Exadata, Goldengate, EBS Consultancy and Training you can send my email adress [email protected].- -Oracle DBA, SQL Server DBA, APPS DBA, Exadata, Goldengate, EBS ve linux Danışmanlık ve Eğitim için [email protected] a mail atabilirsiniz.
While I was testing something on a 12.1 test database got this below error whenever I’m trying to execute specific admin commands:
SQL> drop user xx;
drop user xx
*
ERROR at line 1:
ORA-04088: error during execution of trigger ‘SYS.XDB_PI_TRIG’
ORA-00604: error occurred at recursive SQL level 1
ORA-06550: line 3, column 13:
PLS-00302: component ‘IS_VPD_ENABLED’ must be declared
ORA-06550: line 3, column 5:
PL/SQL: Statement ignored
SQL> alter table bb move online compress;
alter table bb move online compress
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-04088: error during execution of trigger ‘SYS.XDB_PI_TRIG’
ORA-00604: error occurred at recursive SQL level 2
ORA-06550: line 3, column 13:
PLS-00302: component ‘IS_VPD_ENABLED’ must be declared
ORA-06550: line 3, column 5:
PL/SQL: Statement ignored
The above was just a sample but the error with showing up with lots of admin commands!
I checked the trigger SYS.XDB_PI_TRIG which causing this error and it was already valid, so I decided to DISABLE it, and then admin commands ran as usual:
SQL> alter trigger SYS.XDB_PI_TRIG disable;
Trigger altered.
Above failing admin commands have run smoothly:
SQL> alter table bb move online compress;
Table altered.
Frankly speaking, I tried to google that error without any success, I didn’t dig deeper, so I took the shortest/laziest way and disabled the root cause trigger as a dirty fix, the database where I disabled that trigger was a test DB, most probably one of my fancy test scenarios caused this issue to happen.
In case you have the same error on a Production Database I strongly recommend you to contact Oracle Support before disabling the above-mentioned trigger.
Update: I’ve dug more and found that the root cause was that someone created a table with the name «sys» under SYS user. Bug 17431402 yes it’s a bug because the engine should throw an error if someone tries to create an object with a «reserved word».
I’ve dropped that object «sys.sys» and the error disappeared:
SQL> alter trigger SYS.XDB_PI_TRIG disable;
Trigger altered.
SQL> drop table sys.sys;
Table dropped.
SQL> alter trigger SYS.XDB_PI_TRIG enable;
Trigger altered.
SQL> alter table bb move online compress;
Table altered.
phew!
Problem Description
-------------------------------
You are creating a trigger that includes an exception handling block. You wish to raise a user defined error when a certain condition is met within the trigger body using keyword RAISE.
Inside your error handling block you also include a call to RAISE_APPLICATION_ERROR.
Consider this code example --
create table tmp (col1 char(40));create table violations (col1 varchar2(30));
CREATE OR REPLACE TRIGGER DEMO_RULE_001
BEFORE INSERT OR UPDATE ON TMP
FOR EACH ROW
DECLARE
RULE_001 EXCEPTION;
BEGIN
IF :NEW.col1 = 'mike' THEN
dbms_output.put_line(:new.col1);
INSERT INTO VIOLATIONS values ('violation logged');
-- Raise rule
RAISE RULE_001;
END IF;
EXCEPTION
WHEN RULE_001 THEN
RAISE_APPLICATION_ERROR (-20001,'Guideline Violation, Rule-001.');
END;
When this trigger is executed, you receive the ora-4088 and ora-6512 errors.
ORA-04088: error during execution of trigger 'SCOTT.DEMO_RULE_001'
Solution Description-------------------------------
You cannot use both RAISE, within the execution block of a trigger, and RAISE_APPLICATION_ERROR, within the exception block.Explanation------------------------
RAISE forces execution to move into the exception block.RAISE_APPLICATION_ERROR, within the exception block, terminates the program.If the trigger body does not complete, the triggering SQL statement and anySQL statements within the trigger body are rolled back. Thus, execution completes unsuccessfully with a runtime error and it appears as if none of the code within the trigger body gets executed.
Consider this corrected code --
CREATE OR REPLACE TRIGGER DEMO_RULE_001
BEFORE INSERT OR UPDATE ON TMP
FOR EACH ROW
DECLARE
RULE_001 EXCEPTION;
BEGIN
IF :NEW.col1 = 'mike' THEN
dbms_output.put_line(:new.col1);
INSERT INTO VIOLATIONS values ('violation logged');
-- Raise rule
RAISE RULE_001;
END IF;
EXCEPTION
WHEN RULE_001 THEN
--raise_application_error(-20001, 'Guideline Violation, Rule-001.');
dbms_output.put_line('Guideline Violation, Rule-001.');
END;
Oracle Support Doc ID 103293.1
Hello, we used exceltable package a few week ago and everything was fine. During this period, our oracle admins applied some patch or etc. and now we get an oracle error ORA-04088 which is connected with trigger checking grants to PUBLIC. I don’t know why something should grant privileges to PUBLIC, but it seems that error emerges on this row — «open l_rc for l_query using p_file, p_method, p_password;» in first getCursor function in EXCELTABLE package. We use ORACLE DB 12.2. Do you have any idea where is granting privileges used and why emerges this error? Thank you in advance.
- This is part of our SQL code:
declare
piv_excel_name ext_ds_upt_kalendar_pro_kl.nazev_souboru%TYPE := ‘Kalendar.xlsx’;
piv_nazev_listu ext_ds_upt_kalendar_pro_kl.nazev_listu%TYPE := ‘Plan’;
cv_db_adresar CONSTANT VARCHAR2(16) := ‘DB_DIR’;
lvr_data SYS_REFCURSOR;
begin
lvr_data :=
ExcelTable.getCursor(
p_file => ExcelTable.getFile(cv_db_adresar, piv_excel_name)
, p_sheet => piv_nazev_listu
, p_cols => ‘»A2″ VARCHAR2(100) column »A»’
, p_range => ‘A2:A2’
);
end;
- This is whole error message:
Error report —
ORA-04088: error during execution of trigger ‘APPLDBA_P.BEFORE_GRANT_PUBLIC’
ORA-00604: error occurred at recursive SQL level 3
ORA-20997: Public grants on data schema objects not allowed
ORA-06512: on line 23
ORA-06512: on «EXT_STAGE.EXCELTABLE», line 4087
ORA-06512: on line 11
- 00000 — «error during execution of trigger ‘%s.%s’»
*Cause: A runtime error occurred during execution of a trigger.
*Action: Check the triggers which were involved in the operation.
- This is the trigger mentioned above:
create or replace TRIGGER appldba_p.before_grant_public BEFORE GRANT ON DATABASE
declare
vLst ora_name_list_t;
vCnt int;
function is_authorized(p_owner varchar2,p_grantor varchar2) return varchar2 is
vRet varchar2(1);
begin
select decode(max(profile),’DAT_USER_PROFILE’,’F’,’T’) into vRet from dba_users where username=p_owner;
if vRet=’F’ then
select decode(count(*),0,’F’,’T’) into vRet from dba_role_privs where granted_role=’DBA’ and grantee=p_grantor;
end if;
return vRet;
end;
begin
if ora_dict_obj_name is null then
return;
end if;
vCnt:=ora_grantee(vLst);
for i in 1..nvl(vCnt,0) loop
if vLst(i)=’PUBLIC’ and is_authorized(ora_dict_obj_owner,ora_login_user)=’F’ then
raise_application_error(-20997,’Public grants on data schema objects not allowed’);
end if;
end loop;
end;
14.12.2010, 22:58. Показов 10932. Ответов 8
Вечер добрый. Создал триггер, который на Insert должен проверять есть-ли запись с некоторыми данными, если уже есть , то выдать ошибку.
SQL | ||
|
В таблице Party — содержатся номера игр , в Game- положение фигур в игре, при этом на доске не должно быть 2-х фигур одинакового типа одного цвета. Создал триггер , добавил записи:
SQL | ||
|
После этого пытаюсь создать еще одну фигуру на доске
SQL | ||
|
Получаю следующее сообщение
Error at line 1:
ORA-20999:Figure with this color already exis
ORA-06512: at «SYSTEM.Game_U», line 9
ORA-04088: error during execution trigger «SYSTEM.Game_U»
Триггер вроде правильный. Помогите пожалуйста разобраться,в чем может быть проблема?
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
0
Одна из моих функций обновления не может быть выполнена, потому что приведенный ниже триггер выдает исключение ORA-2001, ORA-06512 и ORA-04088.
create or replace TRIGGER "UAM_USER"."BEFORE_UPDATE_VILLAGE" BEFORE UPDATE ON village
FOR EACH ROW
DECLARE
v_tbl_id NUMBER(2);
user_xcep EXCEPTION;
PRAGMA EXCEPTION_INIT( user_xcep, -20001 );
BEGIN
IF :OLD.panchayat_id != :NEW.panchayat_id
THEN
RAISE user_xcep;
END IF;
v_tbl_id := 14;
IF :OLD.name != :NEW.name
THEN
INSERT INTO UAM_USER.MASTER_HISTORY
(HISTORY_ID
,TBL_ID
,USER_ID
,FIELD_NAME
,OLD_VALUE
,NEW_VALUE
,HISTORY_DATE
,USER_NAME
,record_id)
VALUES
(MASTER_HISTORY_SEQ.NEXTVAL
,v_tbl_id
,:NEW.UPDATE_BY
,'Name'
,:OLD.name
,:NEW.name
,:NEW.UPDATE_DATE
,:NEW.update_by_name
,:NEW.village_id
);
END IF;
IF (:OLD.name_hindi IS NULL AND :NEW.name_hindi IS NOT NULL)
OR (:NEW.name_hindi IS NULL AND :OLD.name_hindi IS NOT NULL)
OR (:OLD.name_hindi != :NEW.name_hindi)
THEN
INSERT INTO UAM_USER.MASTER_HISTORY
(HISTORY_ID
,TBL_ID
,USER_ID
,FIELD_NAME
,OLD_VALUE
,NEW_VALUE
,HISTORY_DATE
,USER_NAME
,record_id)
VALUES
(MASTER_HISTORY_SEQ.NEXTVAL
,v_tbl_id
,:NEW.UPDATE_BY
,'Name Hindi'
,:OLD.name_hindi
,:NEW.name_hindi
,:NEW.UPDATE_DATE
,:NEW.update_by_name
,:NEW.village_id
);
END IF;
IF (:OLD.status IS NULL AND :NEW.status IS NOT NULL)
OR (:NEW.status IS NULL AND :OLD.status IS NOT NULL)
OR (:OLD.status != :NEW.status)
THEN
INSERT INTO UAM_USER.MASTER_HISTORY
(HISTORY_ID
,TBL_ID
,USER_ID
,FIELD_NAME
,OLD_VALUE
,NEW_VALUE
,HISTORY_DATE
,USER_NAME
,record_id)
VALUES
(MASTER_HISTORY_SEQ.NEXTVAL
,v_tbl_id
,:NEW.UPDATE_BY
,'Status'
,:OLD.status
,:NEW.status
,:NEW.UPDATE_DATE
,:NEW.update_by_name
,:NEW.village_id
);
END IF;
END;
ЖУРНАЛ КОНСОЛИ:
org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [ UPDATE village SET name = ? ,panchayat_id = ? ,name_hindi = ? ,status = ? ,update_date = ? ,update_by = ? ,update_by_name = ? ,dc_id = ? ,loksabha_id = ? ,census_code = ? ,pincode = ? ,area_type = ? WHERE village_id = ? ]; SQL state [72000]; error code [20001]; ORA-20001:
ORA-06512: at "UAM_USER.BEFORE_UPDATE_VILLAGE", line 9
ORA-04088: error during execution of trigger 'UAM_USER.BEFORE_UPDATE_VILLAGE'
; nested exception is java.sql.SQLException: ORA-20001:
ORA-06512: at "UAM_USER.BEFORE_UPDATE_VILLAGE", line 9
ORA-04088: error during execution of trigger 'UAM_USER.BEFORE_UPDATE_VILLAGE'
Я не могу понять это, я проверяю все свои параметры в своем контроллере, все параметры в порядке, и в моем контроллере и сервисе нет проблем, проблема выше триггера и согласно моим исследованиям и разработкам коды ошибок {{X0} } и ORA-06512
представляют ошибку относительно ширины столбца, а ORA-4088
представляют рекурсивное обновление.
ОБНОВЛЕНИЕ КОДА:
@Override
public void update(Village village) {
String q = " UPDATE village "
+ " SET name = :name "
// + " ,panchayat_id = :panchayat_id "
+ " ,name_hindi = :name_hindi "
+ " ,status = :status "
+ " ,update_date = :update_date "
+ " ,update_by = :update_by "
+ " ,update_by_name = :update_by_name "
+ " ,dc_id = :dc_id "
+ " ,loksabha_id = :loksabha_id "
+ " ,census_code = :census_code "
+ " ,pincode = :pincode "
+ " ,area_type = :area_type "
+ " WHERE village_id = :village_id ";
MapSqlParameterSource param = new MapSqlParameterSource();
param.addValue("village_id", village.getVillageId());
param.addValue("panchayat_id", village.getPanchayat() != null ? village.getPanchayat().getPanchayatId() : null);
param.addValue("name", village.getName());
param.addValue("name_hindi", village.getNameHindi());
param.addValue("status", village.getStatus());
param.addValue("update_date", village.getUpdateDate());
param.addValue("update_by", village.getUserdetail() != null ? village.getUserdetail().getUserId() : null);
param.addValue("update_by_name", village.getUserdetail().getName());
param.addValue("dc_id", village.getDc() != null ? village.getDc().getDcId() : null);
param.addValue("loksabha_id", village.getLoksabha() != null ? village.getLoksabha().getLoksabhaId() : null);
param.addValue("census_code", village.getCensusCode());
param.addValue("pincode", village.getPincode());
param.addValue("area_type", village.getAreaType());
getNamedParameterJdbcTemplate().update(q, param);
}
Я проверяю свои столбцы, все в порядке, поэтому все, что осталось, это ORA-04088, и я не могу понять, где и как он рекурсивно пытается обновить таблицы.
Пожалуйста помоги
ORA-06512 — это стандартная ошибка PL / SQL, когда вызываемая процедура (или триггер) не работает.
Ora-04088 — это стандартная ошибка, которую Oracle выдает, когда выполняющийся триггер получает ошибку времени выполнения.
Основная причина — ошибка ORA-20001. Это диапазон исключений, определяемых пользователем, и вот! ваш триггер определяет один:
PRAGMA EXCEPTION_INIT( user_xcep, -20001 );
Триггер вызывает это исключение здесь:
IF :OLD.panchayat_id != :NEW.panchayat_id
THEN
RAISE user_xcep;
END IF;
Похоже, ваш оператор обновления пытается изменить значение panchayat_id
. Лучшим решением было бы удалить этот столбец из предложения SET:
SET name = ? ,panchayat_id = ?
Глядя на свой SpringBatch (или любой другой) код, возможно, вам стоит изменить проверку валидации. Возможно что-то вроде этого:
IF :OLD.panchayat_id is null then
null; -- allow update
ELSIF :NEW.panchayat_id is null then
:NEW.panchayat_id := :OLD.panchayat_id; -- don't wipe existing value
ELSIF :OLD.panchayat_id != :NEW.panchayat_id
THEN
RAISE user_xcep;
END IF;
3
APC
11 Окт 2018 в 09:46
Согласно документации:
ORA-06512: в строке
Причина: сообщение Backtrace, поскольку стек разматывается необработанными исключениями.
По сути, эта ошибка является частью стека ошибок, сообщающего, в какой строке произошла фактическая ошибка.
И документация:
ORA-04088: ошибка во время выполнения триггера ‘string.string’
Причина. Произошла ошибка во время выполнения триггера.
И эта ошибка является частью стека ошибок, сообщающего вам, что ошибка действительно произошла в триггере.
При возникновении необработанной ошибки всегда отображается стек ошибок. Если вы хотите отобразить только сообщение об ошибке, вы можете использовать часть обработки исключений, чтобы тело триггера выглядело примерно так:
begin
if upper(:new.name) = 'TEST' then
raise_application_error(-20001, 'Sorry, that value is not allowed.');
end if;
exception
when others then
dbms_output.put_line(sqlcode|' '|sqlerrm);
end;
Ключевые слова для поиска сведений о значениях ошибок Oracle в диапазоне ORA-04000 — ORA-04999:
На русском языке: ошибки Oracle, коды оракловых ошибок;
На английском языке: ORA-04000 — ORA-04999.