ТРИГГЕРЫ, которые приводят к сбою INSERT? Возможное?

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

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

Ответ 1

Из этого блога

Триггеры MySQL: как прервать операцию INSERT, UPDATE или DELETE с помощью триггера? На EfNets #mysql кто-то спросил:

Как сделать триггер, прервавший операцию, если мое бизнес-правило не выполняется?

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

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

Возможно, MySQL 5.2 будет включать в себя оператор SIGNAL, который сделает этот хак украденным прямо из программирования хранимых процедур 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;

Ответ 2

Вот как я это сделал. Обратите внимание на 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 ;

Ответ 3

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

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

Ответ 4

Это отменит ваш INSERT, создав исключение (из 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!');

Ответ 5

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

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

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

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

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

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

Ответ 6

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

IF UPDATE(column_name)
BEGIN
  RAISEERROR
  ROLLBACK TRAN
  RETURN
END

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