ТРИГГЕРЫ, которые вызывают сбой ВСТАВКИ? Возможный? - PullRequest
14 голосов
/ 23 октября 2008

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

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

Ответы [ 6 ]

19 голосов
/ 23 октября 2008

Из этого сообщения в блоге

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

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

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

СП 11. Имеются ли в СП инструкции «поднять», чтобы «поднять ошибки приложения»? Извините, не в настоящее время. Стандартные операторы SQL SIGNAL и RESIGNAL находятся в TODO.

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

Вот как я это сделал. Обратите внимание на SET NEW='some error';. MySQL скажет вам, что «Переменная« 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 ;
4 голосов
/ 30 сентября 2014

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

Если есть ошибка, вы можете заставить MySQL использовать SIGNAL , но если вы не укажете его как класс как 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 +.

1 голос
/ 04 июля 2012

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

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

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

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

PROCEDURE administrator.TEST_CANNOT_BE_SET_TO_1 не существует

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

1 голос
/ 23 октября 2008

Это прервет вашу INSERT, вызвав исключение (из http://www.experts -exchange.com / База данных / 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!');
0 голосов
/ 23 октября 2008

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

IF UPDATE(column_name)
BEGIN
  RAISEERROR
  ROLLBACK TRAN
  RETURN
END

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

...