Вызов ошибки в триггере MySQL
Если у меня есть trigger
before the update
на таблице, как я могу бросить ошибку, которая предотвращает обновление на этой таблице?
6 ответов:
вот один хак это может сработать. Это не чисто, но похоже, что это может сработать:
по сути, вы просто пытаетесь обновить столбец, который не существует.
С MySQL 5.5, вы можете использовать
SIGNAL
синтаксис для создания исключения:signal sqlstate '45000' set message_text = 'My Error Message';
состояние 45000 является общим состоянием, представляющим "необработанное пользовательское исключение".
вот более полный пример такого подхода:
delimiter // use test// create table trigger_test ( id int not null )// drop trigger if exists trg_trigger_test_ins // create trigger trg_trigger_test_ins before insert on trigger_test for each row begin declare msg varchar(128); if new.id < 0 then set msg = concat('MyTriggerError: Trying to insert a negative value in trigger_test: ', cast(new.id as char)); signal sqlstate '45000' set message_text = msg; end if; end // delimiter ; -- run the following as seperate statements: insert into trigger_test values (1), (-1), (2); -- everything fails as one row is bad select * from trigger_test; insert into trigger_test values (1); -- succeeds as expected insert into trigger_test values (-1); -- fails as expected select * from trigger_test;
к сожалению, ответ, предоставленный @RuiDC, не работает в версиях MySQL до 5.5, потому что нет реализации сигнал для хранимых процедур.
решение у меня нашел имитировать сигнал бросая
table_name doesn't exist
ошибка, нажав настроенное сообщение об ошибке вtable_name
.взлом может быть реализован с помощью триггеров или с помощью хранимой процедуры. Я описываю оба варианта ниже, следуя примеру, используемому @RuiDC.
используя триггеры
DELIMITER $$ -- before inserting new id DROP TRIGGER IF EXISTS before_insert_id$$ CREATE TRIGGER before_insert_id BEFORE INSERT ON test FOR EACH ROW BEGIN -- condition to check IF NEW.id < 0 THEN -- hack to solve absence of SIGNAL/prepared statements in triggers UPDATE `Error: invalid_id_test` SET x=1; END IF; END$$ DELIMITER ;
использование хранимой процедуры
хранимые процедуры позволяют использовать динамический sql, что делает возможным инкапсуляцию функции генерации ошибок в одной процедуре. Контрапункт заключается в том, что мы должны контролировать методы вставки/обновления приложений, поэтому они используют только нашу хранимую процедуру (не предоставляя прямых привилегий для вставки/обновления).
DELIMITER $$ -- my_signal procedure CREATE PROCEDURE `my_signal`(in_errortext VARCHAR(255)) BEGIN SET @sql=CONCAT('UPDATE `', in_errortext, '` SET x=1'); PREPARE my_signal_stmt FROM @sql; EXECUTE my_signal_stmt; DEALLOCATE PREPARE my_signal_stmt; END$$ CREATE PROCEDURE insert_test(p_id INT) BEGIN IF NEW.id < 0 THEN CALL my_signal('Error: invalid_id_test; Id must be a positive integer'); ELSE INSERT INTO test (id) VALUES (p_id); END IF; END$$ DELIMITER ;
следующая процедура (на mysql5) способ бросить пользовательские ошибки и регистрировать их в то же время:
create table mysql_error_generator(error_field varchar(64) unique) engine INNODB; DELIMITER $$ CREATE PROCEDURE throwCustomError(IN errorText VARCHAR(44)) BEGIN DECLARE errorWithDate varchar(64); select concat("[",DATE_FORMAT(now(),"%Y%m%d %T"),"] ", errorText) into errorWithDate; INSERT IGNORE INTO mysql_error_generator(error_field) VALUES (errorWithDate); INSERT INTO mysql_error_generator(error_field) VALUES (errorWithDate); END; $$ DELIMITER ; call throwCustomError("Custom error message with log support.");
CREATE TRIGGER sample_trigger_msg BEFORE INSERT FOR EACH ROW BEGIN IF(NEW.important_value) < (1*2) THEN DECLARE dummy INT; SELECT Enter your Message Here!!! INTO dummy FROM mytable WHERE mytable.id=new.id END IF; END;
другой (hack) метод (если вы не на 5.5+ по какой-то причине), что вы можете использовать:
Если у вас есть обязательное поле, то в триггере установите Обязательное поле в недопустимое значение, такое как NULL. Это будет работать как для вставки, так и для обновления. Отметим, что если null является допустимым значением для поля (по какой-то причине), то этот подход не будет работать.
BEGIN -- Force one of the following to be assigned otherwise set required field to null which will throw an error IF (NEW.`nullable_field_1` IS NULL AND NEW.`nullable_field_2` IS NULL) THEN SET NEW.`required_id_field`=NULL; END IF; END
Если вы находитесь на 5.5+, то вы можете использовать состояние сигнала, как описано в других ответы:
BEGIN -- Force one of the following to be assigned otherwise use signal sqlstate to throw a unique error IF (NEW.`nullable_field_1` IS NULL AND NEW.`nullable_field_2` IS NULL) THEN SIGNAL SQLSTATE '45000' set message_text='A unique identifier for nullable_field_1 OR nullable_field_2 is required!'; END IF; END