2008-10-23 4 views
14

В процессе очистки this answer Я узнал немного о TRIGGER и хранил процедуры в MySQL, но был ошеломлен тем, что в то время как триггеры BEFORE INSERT и BEFORE UPDATE могут изменять данные, они, похоже, не могут привести к сбою вставки/обновлению (т.е. , В этом конкретном случае я смог заставить это работать, манипулируя данными таким образом, чтобы вызвать дубликат первичного ключа, который в данном конкретном случае имел смысл, но не обязательно имеет смысл в общем смысле.ТРИГГЕРЫ, которые приводят к сбою INSERT? Возможное?

Возможно ли такое функционирование в MySQL? В любой другой СУБД (мой опыт ограничен MySQL с грустью)? Возможно, синтаксис стиля THROW EXCEPTION?

+1

с MySQL 5.5, возможно, и раньше, вы можете использовать сигналы, [см мой ответ здесь] [1] [1]: http://stackoverflow.com/questions/24/throw-error- in-mysql-trigger/7189396 # 7189396 – RuiDC 2011-08-25 11:18:22

ответ

19

Из этого blog post

MySQL Triggers: How do you abort an INSERT, UPDATE or DELETE with a trigger? On EfNet’s #mysql someone asked:

How do I make a trigger abort the operation if my business rule fails?

In MySQL 5.0 and 5.1 you need to resort to some trickery to make a trigger fail and deliver a meaningful error message. The MySQL Stored Procedure FAQ says this about error handling:

SP 11. Do SPs have a “raise” statement to “raise application errors”? Sorry, not at present. The SQL standard SIGNAL and RESIGNAL statements are on the TODO.

Perhaps MySQL 5.2 will include SIGNAL statement which will make this hack stolen straight from MySQL Stored Procedure Programming obsolete. What is the hack? You’re going to force MySQL to attempt to use a column that does not exist. Ugly? Yes. Does it work? Sure.

CREATE TRIGGER mytabletriggerexample 
BEFORE INSERT 
FOR EACH ROW BEGIN 
IF(NEW.important_value) < (fancy * dancy * calculation) THEN 
    DECLARE dummy INT; 

    SELECT Your meaningful error message goes here INTO dummy 
     FROM mytable 
     WHERE mytable.id=new.id 
END IF; END; 
+5

Я действительно рассмеялся, читая этот пример ... Это потрясающий взлом, который играет на сообщениях об ошибках MySQL ... Вот как-то скоро понадобятся для SIGNAL. – 2008-10-23 14:02:16

+0

Это так здорово. Я установил для некоторого столбца NOT NULL значение NULL, но это решение выдает осмысленное сообщение об ошибке. +1! – 2008-10-23 16:17:44

+1

Это не подходит для меня, когда инструкция declare находится внутри условия. Документы говорят, что [DECLARE разрешено только внутри инструкции BEGIN ... END и должно быть в начале, перед любыми другими утверждениями.] (Http://dev.mysql.com/doc/refman/5.0/en/ declare.html). – 2012-07-07 14:19:26

1

Это прервет Добавленное путем повышения исключение (из http://www.experts-exchange.com/Database/MySQL/Q_23788965.html)

DROP PROCEDURE IF EXISTS `MyRaiseError`$$ 

CREATE PROCEDURE `MyRaiseError`(msg VARCHAR(62)) 
BEGIN 
DECLARE Tmsg VARCHAR(80); 
SET Tmsg = msg; 
IF (CHAR_LENGTH(TRIM(Tmsg)) = 0 OR Tmsg IS NULL) THEN 
SET Tmsg = 'ERROR GENERADO'; 
END IF; 
SET Tmsg = CONCAT('@@MyError', Tmsg, '@@MyError'); 
SET @MyError = CONCAT('INSERT INTO', Tmsg); 
PREPARE stmt FROM @MyError; 
EXECUTE stmt; 
DEALLOCATE PREPARE stmt; 
END$$ 

Использование:

call MyRaiseError('Here error message!'); 
8

Вот как я это сделал. Обратите внимание на SET NEW='some error';. MySQL сообщит вам, что «Variable« new »не может быть установлен в значение« Ошибка: не удается удалить этот элемент. В таблице продаж с этим элементом есть записи в таблице продаж ».

Вы можете заманить это в свой код а затем показать результирующую ошибку :)

DELIMITER $$ 
DROP TRIGGER IF EXISTS before_tblinventoryexceptionreasons_delete $$ 
CREATE TRIGGER before_tblinventoryexceptionreasons_delete 
BEFORE DELETE ON tblinventoryexceptionreasons 
FOR EACH ROW BEGIN 
    IF (SELECT COUNT(*) FROM tblinventoryexceptions WHERE tblinventoryexceptions.idtblinventoryexceptionreasons = old.idtblinventoryexceptionreasons) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the inventory exception reasons table with this item.'; 
    END IF; 
END$$ 
DELIMITER ; 

DELIMITER $$ 
DROP TRIGGER IF EXISTS before_storesalesconfig_delete $$ 
CREATE TRIGGER before_storesalesconfig_delete 
BEFORE DELETE ON tblstoresalesconfig 
FOR EACH ROW BEGIN 
    IF (SELECT COUNT(*) FROM tblstoresales WHERE tblstoresales.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the sales table with this item.'; 
    END IF; 
    IF (SELECT COUNT(*) FROM tblinventory WHERE tblinventory.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the inventory table with this item.'; 
    END IF; 
    IF (SELECT COUNT(*) FROM tblinventoryexceptions WHERE tblinventoryexceptions.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the inventory exceptions table with this item.'; 
    END IF; 
    IF (SELECT COUNT(*) FROM tblinvoicedetails WHERE tblinvoicedetails.idtblstoresalesconfig=old.idtblstoresalesconfig) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the inventory details table with this item.'; 
    END IF; 
END$$ 
DELIMITER ; 

DELIMITER $$ 
DROP TRIGGER IF EXISTS before_tblinvoice_delete $$ 
CREATE TRIGGER before_tblinvoice_delete 
BEFORE DELETE ON tblinvoice 
FOR EACH ROW BEGIN 
    IF (SELECT COUNT(*) FROM tblinvoicedetails WHERE tblinvoicedetails.idtblinvoice = old.idtblinvoice) > 0 
    THEN 
    SET NEW='Error: Cannot delete this item. There are records in the inventory details table with this item.'; 
    END IF; 
END$$ 
DELIMITER ; 
1

doen't работу в триггерах (Dynamic SQL не допускаются в хранимой функции или триггере)

Я использую очень грязный раствор:

If NEW.test=1 then CALL TEST_CANNOT_BE_SET_TO_1; end if;

Когда тест = 1 Mysql бросает следующее исключение:

ПРОЦЕДУРА administratie.TEST_CANNOT_BE_SET_TO_1 не существует

Не сложный, но быстро и полезно.

3

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

Если есть ошибка, вы можете заставить MySQL использовать SIGNAL, но если вы не укажете его как класс SQLEXCEPTION, то ничего не произойдет, поскольку не все SQLSTATE считаются плохими, и даже тогда вы 'd должен убедиться, что RESIGNAL, если у вас есть вложенные блоки BEGIN/END.

В качестве альтернативы, и, возможно, еще проще, в пределах вашего триггера, объявить обработчик выхода и отменить исключение.

CREATE TRIGGER `my_table_AINS` AFTER INSERT ON `my_table` FOR EACH ROW 
BEGIN 
    DECLARE EXIT HANDLER FOR SQLEXCEPTION 
     RESIGNAL; 
    DECLARE EXIT HANDLER FOR SQLWARNING 
     RESIGNAL; 
    DECLARE EXIT HANDLER FOR NOT FOUND 
     RESIGNAL; 
    -- Do the work of the trigger. 
END 

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

Это работает с любой версией 5.5+.

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