MySQL Before Delete触发器可避免删除多行

时间:2018-11-02 14:28:37

标签: mysql triggers sql-delete delete-row

我正在尝试通过使用BEFORE DELETE触发器来避免一次在MySQL中删除多于一行的内容。

示例表和触发器如下。

表测试:

DROP TABLE IF EXISTS `test`;
CREATE TABLE `test` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `a` int(11) NOT NULL,
 `b` int(11) NOT NULL,
 PRIMARY KEY (`id`));


INSERT INTO `test` (`id`, `a`, `b`)
VALUES (1, 1, 2);

INSERT INTO `test` (`id`, `a`, `b`)
VALUES (2, 3, 4);

触发:

DELIMITER //
DROP TRIGGER IF EXISTS prevent_multiple_deletion;
CREATE TRIGGER prevent_multiple_deletion
BEFORE DELETE ON test
FOR EACH STATEMENT 
BEGIN

  IF(ROW_COUNT()>=2) THEN  
    SIGNAL SQLSTATE '45000' 
    SET MESSAGE_TEXT = 'Cannot delete more than one order per time!';
  END IF;

END //

DELIMITER ;

这仍然允许删除多行。即使我将IF更改为> = 1,仍然允许该操作。

我的想法是避免进行以下操作:

DELETE FROM `test` WHERE `id`< 5;

你能帮我吗?我知道当前版本的MySQL不允许FOR EACH STATEMENT触发器。

谢谢!

1 个答案:

答案 0 :(得分:1)

首先,从您的原始尝试中消除一些语法错误:

  • 应该是FOR EACH STATEMENT,而不是FOR EACH ROW
  • 由于您已经将定界符定义为//;您需要在//语句中使用;(而不是DROP TRIGGER IF EXISTS ..)。
  • Row_Count()Before Delete Trigger中的值为0,因为尚未更新任何行。所以这种方法行不通。

现在,这里的诀窍是使用会话级可访问(持久) user-defined variables。我们可以定义一个变量,例如@rows_being_deleted,然后检查它是否已经定义。

For Each Row对要删除的每一行运行相同的语句集。因此,我们将只检查会话变量是否已经存在。如果没有,我们可以定义它。因此,基本上,对于第一行(将被删除),将对其进行定义,只要存在会话,该行将一直存在。

现在,如果要删除的行更多,则Trigger将为其余行运行相同的语句集。在第二行中,现在可以找到先前定义的变量,现在我们可以简单地引发一个异常。

注意,在同一会话中可能会触发多个删除语句。因此,在引发异常之前,我们需要将@rows_being_deleted的值重新设置为null

以下将起作用:

DELIMITER //
DROP TRIGGER IF EXISTS prevent_multiple_deletion //
CREATE TRIGGER prevent_multiple_deletion
  BEFORE DELETE ON `test`
  FOR EACH ROW  
    BEGIN

       -- check if the variable is already defined or not
       IF( @rows_being_deleted IS NULL ) THEN 
         SET @rows_being_deleted = 1; -- set its value

       ELSE -- it already exists and we are in next "row"

         -- just for testing to check the row count
         -- SET @rows_being_deleted = @rows_being_deleted + 1;

         -- We have to reset it to null, as within same session
         -- another delete statement may be triggered.
            SET @rows_being_deleted = NULL;

         -- throw exception
         SIGNAL SQLSTATE '45000' 
         SET MESSAGE_TEXT = 'Cannot delete more than one order per time!';
       END IF;

  END //

DELIMITER ;

DB Fiddle Demo 1 :尝试删除多个行。

DELETE FROM `test` WHERE `id`< 5;

结果:

  

查询错误:错误:ER_SIGNAL_EXCEPTION:不能删除多个   每次订购!


DB Fiddle Demo 2 :尝试仅删除一行

查询#1

DELETE FROM `test` WHERE `id` = 1;
  

删除成功发生。我们可以使用Select检查其余行。

查询#2

SELECT * FROM `test`;

| id  | a   | b   |
| --- | --- | --- |
| 2   | 3   | 4   |