导致INSERT失败的TRIGGER?可能?

时间:2008-10-23 13:46:28

标签: mysql triggers rdbms

在清理this answer时,我在MySQL中学到了一些关于TRIGGER和存储过程的知识,但我惊讶地发现,BEFORE INSERTBEFORE UPDATE触发器可以修改数据,它们似乎不会导致插入/更新失败(即验证)。在这种特殊情况下,我能够通过操纵数据来使这一点工作,从而导致主键复制,在这种特定情况下是有意义的,但在一般意义上不一定有意义。

这种功能在MySQL中是否可行?在任何其他RDBMS(我的经验仅限于MySQL)?也许是THROW EXCEPTION样式语法?

6 个答案:

答案 0 :(得分:19)

从此blog post

  

MySQL触发器:如何使用a中止INSERT,UPDATE或DELETE   触发?在EfNet的#mysql上有人   问:

     

如果我的业务规则失败,如何触发操作会中止操作?

     

在MySQL 5.0和5.1中,您需要   采取一些技巧来制作一个   触发失败并提供有意义的   错误信息。 MySQL存储   程序FAQ说明错误   处理:

     

SP 11. SP是否有“提高”语句来“引发应用程序错误”?对不起,现在不行。 SQL标准SIGNAL和RESIGNAL语句位于TODO上。

     

也许MySQL 5.2将包含SIGNAL   这个hack的声明   直接从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;

答案 1 :(得分:8)

这是我做的方式。请注意SET NEW='some error';。 MySQL将告诉您“变量'新'不能设置为'错误:无法删除此项。销售表中有此项目的记录。'”

您可以在代码中捕获它,然后显示结果错误:)

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 ;

答案 2 :(得分:4)

因为当我在MySQL触发器中搜索错误处理时,这篇文章出现在顶部,我想我会分享一些知识。

如果出现错误,您可以强制MySQL使用SIGNAL,但如果您没有将其指定为SQLEXCEPTION类,则不会发生任何事情,因为并非所有SQLSTATE都被视为错误,并且即使这样,如果你有任何嵌套的BEGIN / END块,你也必须确保RESIGNAL

或者,可能更简单,在触发器中,声明一个退出处理程序并重新签名异常。

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 +。

答案 3 :(得分:1)

这将通过引发异常(来自http://www.experts-exchange.com/Database/MySQL/Q_23788965.html

来中止您的INSERT
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!');

答案 4 :(得分:1)

不要在触发器中工作 (存储函数或触发器中不允许使用动态SQL)

我使用非常脏的解决方案:

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

当test = 1时,Mysql抛出以下异常:

PROCEDURE administratie.TEST_CANNOT_BE_SET_TO_1不存在

不复杂但快速且有用。

答案 5 :(得分:-1)

在MS SQL中,您可以使用正确的语法使其工作:

IF UPDATE(column_name)
BEGIN
  RAISEERROR
  ROLLBACK TRAN
  RETURN
END

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