Триггеры, которые вызывают сбой вставок? Возможно?

в очистке ответ я узнал немного о TRIGGERs и хранимые процедуры в MySQL, но был ошеломлен, что, в то время как BEFORE INSERT и BEFORE UPDATE триггеры могут изменять данные, они, по-видимому, не могут вызвать сбой вставки/обновления (т. е. утверждение.) В этом конкретном случае я смог заставить это работать, манипулируя данными таким образом, чтобы вызвать дубликат первичного ключа, что в данном конкретном случае имело смысл, но не обязательно имеет смысл в общем чувство.

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

6 ответов


от этого блоге

MySQL Triggers: как вы прерываете вставку, обновление или удаление с помощью триггер? На EfNet #mysql кто-то спросил:

как сделать триггер прервать операцию, если мое бизнес-правило терпит неудачу?

в MySQL 5.0 и 5.1 вам нужно прибегнуть к хитрости, чтобы сделать сбой триггера и предоставление значимого сообщение об ошибке. Сохраненный MySQL Процедура FAQ говорит об этом ошибка обращение:

SP 11. Есть ли у SPs оператор "raise "для"повышения ошибок приложения"? Извините, не сейчас. Стандартные операторы SIGNAL и RESIGNAL SQL находятся на TODO.

возможно, MySQL 5.2 будет включать сигнал заявление, которое сделает этот хак украдено прямо из MySQL хранится Процедура программирования устарела. Что? это взлом? Ты заставишь MySQL пытаться использовать столбец не существует. Уродливый? Да. Делает он работа? Конечно.

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;

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

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

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 ;

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

Если есть ошибка, Вы можете заставить MySQL использовать сигнал, но если вы не укажете его как класс как SQLEXCEPTION, то ничего не произойдет, так как не все SQLSTATEs считаются плохими, и даже тогда вам придется убедиться, что 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+.


это прервет вашу вставку, вызвав исключение (из 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!');

не работает в триггерах (Динамический SQL не допускается в сохраненной функции или триггера)

Я использую очень грязное решение:

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

когда test=1 Mysql выдает следующее исключение:

процедура администрацие.TEST_CANNOT_BE_SET_TO_1 не существует

Не сложный, но быстрый и удобный.


в MS SQL вы можете заставить его работать, используя правильный синтаксис:

IF UPDATE(column_name)
BEGIN
  RAISEERROR
  ROLLBACK TRAN
  RETURN
END

http://msdn.microsoft.com/en-us/magazine/cc164047.aspx