【问题标题】:Are triggers transaction?是触发器交易吗?
【发布时间】:2016-06-10 13:15:30
【问题描述】:

我在桌子上有一些触发器BEFORE INSERTAFTER DELETE。如何确定触发器是否失败,然后我的查询会回滚?

我的意思是我想确定,查询和触发器都可以工作,或者它们都不起作用。触发器也是事务吗?

【问题讨论】:

标签: mysql sql triggers


【解决方案1】:

来自mysql documentation

对于事务表,语句失败应导致该语句执行的所有更改回滚。触发器失败会导致语句失败,所以触发器失败也会导致回滚。

【讨论】:

  • “对于事务性表” ...您到底是什么意思?如何了解我的表是否是事务性的?
  • 事务表是支持事务的表。当该语句的效果可以提交或回滚时,该语句在事务中执行。
  • @MartinAJ 对于 MySQL,这取决于表的引擎。例如,InnoDB 是事务性的,而 MyISAM 不是。
【解决方案2】:

我可以通过存储过程来展示这一点。这个概念来自 wchiquito 的 answer。我相信您会发现这是一个更详尽的答案。这只是一个例子。为您的特定需求(其他触发器类型)等进行必要的更改。如何执行 mysql 触发器信号触发 outside 不使用存储过程是任何人的猜测。因此,如果您不愿意或无法执行存储过程,请不要继续阅读

请注意,为方便起见,所有删除或截断部分都将保留并删除。

架构

create database trigTranTest;   -- creates a separate database for testing
use trigTranTest;   -- use that database

-- drop table tableA;
create table tableA
(   id int auto_increment primary key,
    something varchar(100) not null,
    age int not null,    -- do not accept unlucky 13
    myDT datetime not null 
);

-- drop table tableB;
create table tableB
(   -- simply to demonstrate multiple tables in a transaction and that they are honored as a group (ie: Transaction)
    -- all or nothing basically
    id int auto_increment primary key,
    something varchar(100) not null,
    myDT datetime not null 
);

-- drop table auditInfoNotInTrans;
create table auditInfoNotInTrans
(   -- a boring table outside of Transaction to show an attempt was made
    id int auto_increment primary key,
    debugInfo varchar(100) not null,
    myDT datetime not null 
);

触发器

-- POINT A
drop trigger if exists tableA_BeforeIns;
DELIMITER $$
create trigger tableA_BeforeIns before insert on tableA
for each row
begin
    if new.age = 13 then
        -- disallow unlucky age=13 for inserts. Wait another year.
        signal sqlstate '45000' set message_text = "tableA_BeforeIns bombed due to age=13";
    end if;
end$$
DELIMITER ;
-- POINT B

关于触发器的快速说明:如果您尝试插入 age=13,则会设置信号。这将启动交易的最终ROLLBACK

请注意,分隔符很重要。要修改上述内容,请突出显示 POINT APOINT B 之间的所有文本并执行它。该块将使用 DELIMITER 后面需要的那种痛苦来执行丢弃和娱乐。如果没有 DELIMITER,错误 1064 即将发生。翻译:这行不通。什么行不通?开始创建触发器的部分。

存储过程

-- POINT A
drop procedure if exists insertChunk;
DELIMITER $$
CREATE PROCEDURE insertChunk(pSomething varchar(100), pAge  int)
    -- takes two parameters, a string for a thing, and an age 
BEGIN
    -- idea lifted from https://stackoverflow.com/a/19908197 by user wchiquito
    -- so spread the appreciation there
    DECLARE `_rollback` BOOL DEFAULT 0;
    DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET `_rollback` = 1;

    -- the following happens outside of the Transaction
    insert auditInfoNotInTrans(debugInfo,myDT) values(pSomething,now());

    -- now our Transaction part begins
    START TRANSACTION;
    insert tableA(something,age,myDT) values (pSomething,pAge,now());   -- pAge being unlucky 13 fails via the Trigger
    IF `_rollback` THEN
        ROLLBACK;
    ELSE
        insert tableB(something,myDT) values (pSomething,now());
        COMMIT;
    END IF;
END$$
DELIMITER ;
-- POINT B

这里有一个简短的说明:一旦出现START TRANSACTION,我们将到达COMMIT,除非我们的触发器发出SQLSTATE信号,从而导致ROLLBACK

如前所述,突出显示并执行POINT APOINT B 中的所有代码以对上述内容进行编辑。这次是存储过程,但类似于之前的create trigger

含义,类似于使用 DELIMITER 块的安全包装触发修改。否则,1064 错误即将到来,存储过程将不会被创建。

测试

请注意,为了您在测试期间的方便,将以下已删除的截断保留在这里。

-- truncate tableA;

-- truncate tableB;

-- truncate auditInfoNotInTrans;
call insertChunk('frog',1);
call insertChunk('lizard',13);  -- force a Trigger failure with the unlucky 13
call insertChunk('snake',2);

结果

select * from auditInfoNotInTrans;
+----+-----------+---------------------+
| id | debugInfo | myDT                |
+----+-----------+---------------------+
|  1 | frog      | 2016-06-10 15:09:02 |
|  2 | lizard    | 2016-06-10 15:09:06 |
|  3 | snake     | 2016-06-10 15:09:08 |
+----+-----------+---------------------+

select * from tableA;
+----+-----------+-----+---------------------+
| id | something | age | myDT                |
+----+-----------+-----+---------------------+
|  1 | frog      |   1 | 2016-06-10 15:09:02 |
|  2 | snake     |   2 | 2016-06-10 15:09:08 |
+----+-----------+-----+---------------------+

select * from tableB;
+----+-----------+---------------------+
| id | something | myDT                |
+----+-----------+---------------------+
|  1 | frog      | 2016-06-10 15:09:02 |
|  2 | snake     | 2016-06-10 15:09:08 |
+----+-----------+---------------------+

结果符合预期,尊重事务处理并且不允许年龄=13 的插入。当然,这是任意的,但我们必须以某种方式对其进行测试。

通过 Mysql Workbench 调用

最后一个视觉。使用 age=13 直接从 Mysql Workbench 运行插入

insert tableA(something,age,myDT) values ('turtle',13,now());

错误代码:1644。tableA_BeforeIns 由于年龄=13 0.000 秒而被炸毁

清理

drop database trigTranTest;

测试数据库已经被删除并且消失了。

【讨论】:

    猜你喜欢
    • 2015-07-15
    • 2010-09-06
    • 1970-01-01
    • 2019-07-27
    • 2017-05-11
    • 1970-01-01
    • 1970-01-01
    • 2014-11-29
    • 1970-01-01
    相关资源
    最近更新 更多