触发器和事务之间的区别

mol*_*ola 1 trigger transaction

我正在实施电影分级服务,所以我正在考虑使用触发器或事务。

我正在考虑的是将电影信息插入到 movie_list 中,并将该电影的类型插入到 movie_genre_list 中。

在这种情况下,我的想法是编写一个触发器,在插入电影信息时插入流派。

但是,我不知道触发器或事务是否适合这种情况。我该怎么办?

Vér*_*ace 13

比较TRIGGERs和TRANSACTIONs就像比较苹果和橘子!不,等等,不是,因为苹果和橙子很相似——它们都是水果,都可以吃。它更像是粉笔和奶酪(英文表达)!

  • 事务用于将操作分组到一个工作单元中,该单元要么全部成功,要么全部失败!

  • 触发器(通常)用于在数据库中发生事件(通常是INSERTUPDATEDELETE在特定表上)时执行(程序/业务逻辑)代码。

TRANSACTIONs。

这些工作单元以独立于其他事务的一致且可靠的方式处理。

来自维基百科:

  1. 提供可靠的工作单元,允许从故障中正确恢复并保持数据库一致,即使在系统故障的情况下,当执行停止(完全或部分)并且对数据库的许多操作仍未完成且状态不明时。

  2. 提供并发访问数据库的程序之间的隔离。如果没有提供这种隔离,程序的结果可能是错误的。

注意“隔离”这个词的使用——这是“ ACID ”测试的标准之一。ACID在这种情况下,既对应于英语单词的游戏,也对应于被认为非常理想的一组数据库事务属性 - 它们是:Atomicicty、Consistency、solationIDurablility。

典型的例子是银行业。假设您想通过从您的当前账户转账来支付 100 欧元的信用卡账单。所以,这是重要两个这个“交易”发生应该作为一个工作单一,一致的单位银行。否则,要么你(钱离开你的活期账户,但没有进入 CC 账户),要么银行(你的 CC 贷记,但你的当前账户没有借记)自掏腰包。

所以,在 SQL 中,你会做的是:

BEGIN TRANSACTION;
UPDATE current_account ca SET ca.balance = ca.balance - 100 WHERE ca.cust_name = 'Haram';
UPDATE      CC_account cc SET cc.balance = cc.balance + 100 WHERE cc.cust_name = 'Haram';
COMMIT;  -- definitively perform the work.
Run Code Online (Sandbox Code Playgroud)

如果任何步骤失败(例如 current_account 中的余额不足),则事务的所有工作都将回滚 - 从而确保一致性。

最后一点 - 如果一切都那么简单就好了!:-) 有(至少)四个复杂因素:

1) 不同的隔离级别。这超出了本答案的范围 - 查看 Wiki 并使用您选择的搜索引擎 - 那里有大量材料,并且

2)供应商的不同实现 - 您必须为此查看自己的系统文档,并且

3)有嵌套TRANSACTIONs 和SAVEPOINTs 之类的东西-同样,这超出了本答案的范围-检查您的文档,并且

4) 有些数据库引擎支持事务性 DDL(数据定义语言),有些(尤其是 MySQL)不支持。

TRIGGERs。

一个数据库TRIGGER,另一方面是不同的动物干脆!ATRIGGER是一个动作(由开发人员编码),它响应表上的事件而发生(同样,有一些复杂的问题,我不会在这里讨论 - 检查 Wiki 和/或使用您的搜索引擎)。在绝大多数情况下,所讨论的事件是桌子上的an INSERT、 anUPDATE或 a DELETE

一个相当不错的类比可以在这里找到。对数据库的操作就像是一排多米诺骨牌——你打倒第一个,其余的就会在连锁反应中倒下。这类似于 aTRIGGER会导致其他表上的更多事件等等!

但是,最好不要将类比过分——您希望尽可能避免进行复杂的多表链式更新——KISS。您可以看到循环引用(TRIGGER在 A UPDATEs B 上,而在A s B 上UPDATE)可能是灾难性的!

这里

您可以使用触发器来执行以下操作,以及此列表中未找到的其他操作:

  • 在数据库中创建活动的审计跟踪。例如,您可以通过将确证信息更新到审计表来跟踪对订单表的更新。

  • 实施业务规则。例如,您可以确定订单何时超出客户的信用额度并显示一条消息以表明该情况。

  • 派生在表或数据库中不可用的附加数据。例如,当items表的quantity列发生更新时,可以计算total_price列的相应调整。

(该链接还提到使用它们来强制执行 DRI(声明性引用完整性) - 我不同意 - 见答案结尾)。

一个典型的(供应商语法在这里有很大差异 - 检查您的系统文档)TRIGGER骨架(从这里获取的示例)将是:

CREATE
  TRIGGER `event_name` BEFORE/AFTER INSERT/UPDATE/DELETE
  ON `database`.`table`
  FOR EACH ROW BEGIN
  -- trigger body
  -- this code is applied to every 
  -- inserted/updated/deleted row
END;
Run Code Online (Sandbox Code Playgroud)

身体可能看起来像这样:

IF NEW.deleted THEN
  SET @changetype = 'DELETE';
ELSE
  SET @changetype = 'NEW';
END IF;
INSERT INTO audit (blog_id, changetype) VALUES (NEW.id, @changetype);
Run Code Online (Sandbox Code Playgroud)

这个触发器是INSERTING一个记录到一个表中audit,用id来自审计表(blog在本例中调用)调用,并且还记录操作是 aDELETE还是 an INSERT。您还可以TIMESTAMP在其中设置逻辑来跟踪操作发生的时间。此外,您可以添加 user_name 逻辑来审核谁INSERTed/ DELETEd 什么以及何时。可能性是无止境。

概括。

因此,总而言之,aTRANSACTION将一组语句分组到一个逻辑单元中,并且 aTRIGGER允许在数据库本身中包含超出 SQL(复杂业务规则)范围的逻辑。

一开始我发现棘手的一件事是(如果没有TRANSACTION指定),服务器将代表您COMMIT在每个语句之后设置一个隐式事务。

例如,这里是 MySQL 服务器的默认行为:

mysql> show variables like '%COMMIT%';
+-----------------------------------------+-------+
| Variable_name                           | Value |
+-----------------------------------------+-------+
| autocommit                              | ON    |
Run Code Online (Sandbox Code Playgroud)

发出命令

mysql> SET autocommit = OFF;
Query OK, 0 rows affected (0.00 sec)
Run Code Online (Sandbox Code Playgroud)

现在,您可以尝试在连续发出多个命令并发出 aCOMMIT以及退出时没有COMMITting时会发生什么。

更好的是,您可以尝试隔离级别

mysql> show variables like '%ISOL%'; 
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
| tx_isolation          | REPEATABLE-READ |
+-----------------------+-----------------+
Run Code Online (Sandbox Code Playgroud)

然后打开不同的终端会话,看看在不同条件下记录可见性会发生什么。

最后一句话:在我看来,您正处于数据库之旅的开始。尽管我已经使用MySQL的例子在这里,我会强烈强烈)要求你使用PostgreSQL只要有可能-这是一个大大优于数据库服务器MySQL和更符合标准的开机!尽可能尝试并使用命令行客户端 - 一开始可能会很繁琐,但从长远来看会更加强大和值得!

非常非常最后的话 (:-) ) - 继续阅读并获得一些好的文本并学习它们!ps 恭喜你回答了关于 dba.stackexchange 的第一个问题!

哎呀 - 差点忘了回答问题!

您可以(如果每部电影只有一种(主要)类型)执行以下操作:

  • 创建一个genre表格并FOREIGN KEY在您的movie表格上指向该genre_name字段(fiddle1),

或者(如果您希望每部电影有不止一种类型 - 一个更有趣和更现实的场景)执行以下操作:

  • 如上所述创建一个genremovie表,但在两者之间也有一个movie_genre连接表(或关联实体)(fiddle2)。