### 25.3.1 触发器语法和示例

要创建触发器或删除触发器，请使用[`创建触发器`](create-trigger.html)要么[`下降触发器`](drop-trigger.html)声明，描述于[第 13.1.22 节，“CREATE TRIGGER 语句”](create-trigger.html)， 和[第 13.1.34 节，“DROP TRIGGER 语句”](drop-trigger.html).

这是一个将触发器与表相关联的简单示例，用于激活[`插入`](insert.html)操作。触发器充当累加器，将插入到表的一列中的值相加。

```
mysql> CREATE TABLE account (acct_num INT, amount DECIMAL(10,2));
Query OK, 0 rows affected (0.03 sec)

mysql> CREATE TRIGGER ins_sum BEFORE INSERT ON account
       FOR EACH ROW SET @sum = @sum + NEW.amount;
Query OK, 0 rows affected (0.01 sec)
```

这[`创建触发器`](create-trigger.html)语句创建一个名为`ins_sum`与`帐户`桌子。它还包括指定触发器操作时间、触发事件以及触发器激活时要执行的操作的子句：

-   关键字`前`表示触发动作时间。在这种情况下，触发器会在每一行插入表之前激活。这里另一个允许的关键字是`后`.

-   关键字`插入`表示触发事件；即激活触发器的操作类型。在示例中，[`插入`](insert.html)操作导致触发器激活。您还可以为[`删除`](delete.html)和[`更新`](update.html)操作。

-   以下声明`每一行`定义触发器主体；也就是说，每次触发器激活时要执行的语句，对于受触发事件影响的每一行都会发生一次。在示例中，触发器主体是一个简单的[`放`](set-variable.html)将插入的值累积到用户变量中`数量`柱子。该声明将该列称为`NEW.金额`这意味着“的价值`数量`要插入新行的列。”

    要使用触发器，请将累加器变量设置为零，执行[`插入`](insert.html)语句，然后查看该变量之后的值：


```
mysql> SET @sum = 0;
mysql> INSERT INTO account VALUES(137,14.98),(141,1937.50),(97,-100.00);
mysql> SELECT @sum AS 'Total amount inserted';
+-----------------------+
| Total amount inserted |
+-----------------------+
|               1852.48 |
+-----------------------+
```

在这种情况下，价值`@和`之后[`插入`](insert.html)已执行的语句是`14.98 + 1937.50 - 100`， 要么`1852.48`.

要销毁触发器，请使用[`下降触发器`](drop-trigger.html)陈述。如果触发器不在默认架构中，则必须指定架构名称：

```
mysql> DROP TRIGGER test.ins_sum;
```

如果您删除一个表，该表的所有触发器也会被删除。

触发器名称存在于模式命名空间中，这意味着所有触发器在模式中必须具有唯一的名称。不同模式中的触发器可以具有相同的名称。

可以为给定表定义具有相同触发事件和操作时间的多个触发器。例如，您可以有两个`更新前`表的触发器。默认情况下，具有相同触发事件和操作时间的触发器按创建顺序激活。要影响触发顺序，请在后面指定一个子句`每一行`这表明`关注`要么`之前`以及现有触发器的名称，该触发器也具有相同的触发器事件和操作时间。和`关注`，新触发器在现有触发器之后激活。和`之前`，新触发器在现有触发器之前激活。

例如，下面的触发器定义定义了另一个`插入前`触发`帐户`桌子：

```
mysql> CREATE TRIGGER ins_transaction BEFORE INSERT ON account
       FOR EACH ROW PRECEDES ins_sum
       SET
       @deposits = @deposits + IF(NEW.amount>0,NEW.amount,0),
       @withdrawals = @withdrawals + IF(NEW.amount<0,-NEW.amount,0);
Query OK, 0 rows affected (0.01 sec)
```

这个触发器，`ins_transaction`， 类似于`ins_sum`但分别累积存款和取款。它有一个`之前`使其在之前激活的子句`ins_sum`;如果没有该子句，它将在之后激活`ins_sum`因为它是在之后创建的`ins_sum`.

在触发器体内，`老的`和`新的`关键字使您能够访问受触发器影响的行中的列。`老的`和`新的`是触发器的 MySQL 扩展；它们不区分大小写。

在一个`插入`触发，仅`新的。*`col_name`*`可以使用；没有旧行。在一个`删除`触发，仅`老的。*`col_name`*`可以使用；没有新行。在一个`更新`触发器，您可以使用`老的。*`col_name`*`在更新之前引用行的列，并且`新的。*`col_name`*`在更新后引用行的列。

一个名为的列`老的`是只读的。你可以参考它（如果你有[`选择`](select.html)特权），但不能修改它。您可以参考名为的列`新的`如果你有[`选择`](privileges-provided.html#priv_select)特权。在一个`前`触发器，你也可以改变它的值`设置新的。*`col_name`* = *`价值`*`如果你有[`更新`](privileges-provided.html#priv_update)特权。这意味着您可以使用触发器来修改要插入新行或用于更新行的值。（这样一个`放`声明在`后`触发，因为行更改已经发生。）

在一个`前`触发器`新的`价值`自动递增`column 为 0，而不是实际插入新行时自动生成的序列号。

通过使用[`开始...结束`](begin-end.html)构造，您可以定义执行多个语句的触发器。内`开始`块，您还可以使用存储例程中允许的其他语法，例如条件和循环。但是，就像存储例程一样，如果您使用[**mysql**](mysql.html)程序要定义一个执行多条语句的触发器，就需要重新定义[**mysql**](mysql.html)语句分隔符，以便您可以使用`;`触发器定义中的语句分隔符。以下示例说明了这些要点。它定义了一个`更新`触发器检查用于更新每一行的新值，并将值修改为 0 到 100 的范围内。这必须是`前`触发器，因为在用于更新行之前必须检查该值：

```
mysql> delimiter //
mysql> CREATE TRIGGER upd_check BEFORE UPDATE ON account
       FOR EACH ROW
       BEGIN
           IF NEW.amount < 0 THEN
               SET NEW.amount = 0;
           ELSEIF NEW.amount > 100 THEN
               SET NEW.amount = 100;
           END IF;
       END;//
mysql> delimiter ;
```

单独定义存储过程然后使用简单的触发器从触发器调用它会更容易[`称呼`](call.html)陈述。如果您想从多个触发器中执行相同的代码，这也是有利的。

触发器在激活时执行的语句中可能出现的内容存在限制：

-   触发器不能使用[`称呼`](call.html)语句来调用将数据返回给客户端或使用动态 SQL 的存储过程。（允许存储过程通过`出去`要么`进出`参数。）

-   触发器不能使用显式或隐式开始或结束事务的语句，例如[`开始交易`](commit.html),[`犯罪`](commit.html)， 要么[`回滚`](commit.html).([`回滚到保存点`](commit.html)是允许的，因为它不会结束事务。）。

    也可以看看[第 25.8 节，“对存储程序的限制”](stored-program-restrictions.html).

    MySQL 在触发器执行期间处理错误如下：

-   如果一个`前`触发器失败，不执行对应行的操作。

-   一种`前`触发器被激活*试图*插入或修改该行，无论随后尝试是否成功。

-   一个`后`只有在任何情况下才会执行触发器`前`触发器和行操作成功执行。

-   在任何一个错误`前`要么`后`trigger 导致导致触发器调用的整个语句失败。

-   对于事务表，一条语句的失败应该导致该语句执行的所有更改的回滚。触发器失败会导致语句失败，因此触发器失败也会导致回滚。对于非事务性表，无法进行此类回滚，因此尽管语句失败，但在错误点之前执行的任何更改仍然有效。

    触发器可以包含按名称对表的直接引用，例如名为`测试参考`如本例所示：


```
CREATE TABLE test1(a1 INT);
CREATE TABLE test2(a2 INT);
CREATE TABLE test3(a3 INT NOT NULL AUTO_INCREMENT PRIMARY KEY);
CREATE TABLE test4(
  a4 INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  b4 INT DEFAULT 0
);

delimiter |

CREATE TRIGGER testref BEFORE INSERT ON test1
  FOR EACH ROW
  BEGIN
    INSERT INTO test2 SET a2 = NEW.a1;
    DELETE FROM test3 WHERE a3 = NEW.a1;
    UPDATE test4 SET b4 = b4 + 1 WHERE a4 = NEW.a1;
  END;
|

delimiter ;

INSERT INTO test3 (a3) VALUES
  (NULL), (NULL), (NULL), (NULL), (NULL),
  (NULL), (NULL), (NULL), (NULL), (NULL);

INSERT INTO test4 (a4) VALUES
  (0), (0), (0), (0), (0), (0), (0), (0), (0), (0);
```

假设您将以下值插入到表中`测试1`如此处所示：

```
mysql> INSERT INTO test1 VALUES
       (1), (3), (1), (7), (1), (8), (4), (4);
Query OK, 8 rows affected (0.01 sec)
Records: 8  Duplicates: 0  Warnings: 0
```

因此，这四个表包含以下数据：

```
mysql> SELECT * FROM test1;
+------+
| a1   |
+------+
|    1 |
|    3 |
|    1 |
|    7 |
|    1 |
|    8 |
|    4 |
|    4 |
+------+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test2;
+------+
| a2   |
+------+
|    1 |
|    3 |
|    1 |
|    7 |
|    1 |
|    8 |
|    4 |
|    4 |
+------+
8 rows in set (0.00 sec)

mysql> SELECT * FROM test3;
+----+
| a3 |
+----+
|  2 |
|  5 |
|  6 |
|  9 |
| 10 |
+----+
5 rows in set (0.00 sec)

mysql> SELECT * FROM test4;
+----+------+
| a4 | b4   |
+----+------+
|  1 |    3 |
|  2 |    0 |
|  3 |    1 |
|  4 |    2 |
|  5 |    0 |
|  6 |    0 |
|  7 |    1 |
|  8 |    1 |
|  9 |    0 |
| 10 |    0 |
+----+------+
10 rows in set (0.00 sec)
```
