14

在清理这个答案时,我了解了一些关于TRIGGERMySQL 中的 s 和存储过程的知识,但令我震惊的是,虽然BEFORE INSERTBEFORE UPDATE触发器可以修改数据,但它们似乎不会导致插入/更新失败(即验证)。在这种特殊情况下,我能够通过以导致主键重复的方式操作数据来使其工作,这在这种特殊情况下是有道理的,但在一般意义上不一定有意义。

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

4

6 回答 6

19

从这篇博文

MySQL 触发器:如何使用触发器中止 INSERT、UPDATE 或 DELETE?在 EfNet 的#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;
于 2008-10-23T13:56:09.007 回答
8

这是我的做法。注意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 ;
于 2010-04-22T20:07:26.583 回答
6

因为这篇文章在我搜索 MySQL 触发器中的错误处理时排名靠前,所以我想我会分享一些知识。

如果出现错误,您可以强制 MySQL 使用SIGNAL,但如果您不将其指定为 SQLEXCEPTION 类,则不会发生任何事情,因为并非所有 SQLSTATE 都被认为是坏的,即使那样您也必须如果您有任何嵌套的 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+ 版本。

于 2014-09-30T07:34:46.340 回答
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!');
于 2008-10-23T13:59:43.680 回答
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 不存在

不复杂但快速且有用。

于 2012-07-03T20:25:40.537 回答
-1

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

IF UPDATE(column_name)
BEGIN
  RAISEERROR
  ROLLBACK TRAN
  RETURN
END

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

于 2008-10-23T14:01:49.917 回答