小巧。快速。可靠。
三选二。
创建触发器

1. 语法

创建触发器语句

CREATE TEMP TEMPORARY TRIGGER IF NOT EXISTS schema-name . trigger-name BEFORE AFTER INSTEAD OF DELETE INSERT UPDATE OF column-name , ON table-name FOR EACH ROW WHEN expr BEGIN update-stmt ; END insert-stmt delete-stmt select-stmt

删除语句

表达式

插入语句

选择语句

更新语句

2. 描述

CREATE TRIGGER 语句用于向数据库模式添加触发器。触发器是在发生指定数据库事件时自动执行的数据库操作。

每个触发器必须指定它将针对以下操作之一触发:DELETEINSERTUPDATE。对于每个被删除、插入或更新的行,触发器都会触发一次。如果使用了“UPDATE OF column-name”语法,则只有当 column-name 出现在 UPDATE 语句的 SET 子句中某个项的左侧时,触发器才会触发。

由于历史上的疏忽,在“UPDATE OF”子句中命名的列实际上不必存在于要更新的表中。无法识别的列名会被静默忽略。如果 SQLite 在“UPDATE OF”子句中的任何名称不是表中的列时,使 CREATE TRIGGER 语句失败,会更有帮助。但是,由于这个问题是在 SQLite 广泛部署多年后才发现的,因此我们一直抵制修复该问题,以免破坏旧版应用程序。

目前,SQLite 仅支持 FOR EACH ROW 触发器,不支持 FOR EACH STATEMENT 触发器。因此,显式指定 FOR EACH ROW 是可选的。FOR EACH ROW 意味着触发器中指定的 SQL 语句可能会(取决于 WHEN 子句)针对导致触发器触发的语句插入、更新或删除的每个数据库行执行。

WHEN 子句和触发器操作都可以使用“NEW.column-name”和“OLD.column-name”形式的引用访问正在插入、删除或更新的行中的元素,其中 column-name 是触发器关联的表的列的名称。OLD 和 NEW 引用只能在与之相关的事件的触发器中使用,如下所示

INSERT NEW 引用有效
UPDATE NEW 和 OLD 引用有效
DELETE OLD 引用有效

如果提供了 WHEN 子句,则只有在 WHEN 子句为真时才会执行指定的 SQL 语句。如果没有提供 WHEN 子句,则每次触发器触发时都会执行 SQL 语句。

BEFORE 或 AFTER 关键字决定相对于关联行的插入、修改或删除何时执行触发器操作。当不存在这两个关键字时,BEFORE 为默认值。

可以在触发器主体内的 UPDATEINSERT 操作的一部分中指定 ON CONFLICT 子句。但是,如果在导致触发器触发的语句的一部分中指定了 ON CONFLICT 子句,则使用外部语句的冲突处理策略。

当与其关联的表(table-name 表)被 删除 时,触发器会自动 删除。但是,如果触发器操作引用其他表,则如果这些其他表被 删除修改,触发器不会被删除或修改。

使用 DROP TRIGGER 语句删除触发器。

2.1. 触发器内 UPDATE、DELETE 和 INSERT 语句的语法限制

触发器内的 UPDATEDELETEINSERT 语句不支持 UPDATEDELETEINSERT 语句的完整语法。适用以下限制

3. INSTEAD OF 触发器

BEFORE 和 AFTER 触发器仅适用于普通表。INSTEAD OF 触发器仅适用于视图。

如果视图上存在 INSTEAD OF INSERT 触发器,则可以对该视图执行 INSERT 语句。不会发生实际的插入。而是运行触发器中包含的语句。INSTEAD OF DELETE 和 INSTEAD OF UPDATE 触发器对视图上的 DELETE 和 UPDATE 语句的工作方式相同。

请注意,sqlite3_changes()sqlite3_total_changes() 接口不会计算 INSTEAD OF 触发器的触发次数,但 count_changes pragma 会计算 INSTEAD OF 触发器的触发次数。

4. 一些触发器示例

假设客户记录存储在“customers”表中,订单记录存储在“orders”表中,则以下 UPDATE 触发器可确保在客户更改其地址时重定向所有关联的订单

CREATE TRIGGER update_customer_address UPDATE OF address ON customers 
  BEGIN
    UPDATE orders SET address = new.address WHERE customer_name = old.name;
  END;

安装此触发器后,执行以下语句

UPDATE customers SET address = '1 Main St.' WHERE name = 'Jack Jones';

会导致自动执行以下操作

UPDATE orders SET address = '1 Main St.' WHERE customer_name = 'Jack Jones';

有关 INSTEAD OF 触发器的示例,请考虑以下模式

CREATE TABLE customer(
  cust_id INTEGER PRIMARY KEY,
  cust_name TEXT,
  cust_addr TEXT
);
CREATE VIEW customer_address AS
   SELECT cust_id, cust_addr FROM customer;
CREATE TRIGGER cust_addr_chng
INSTEAD OF UPDATE OF cust_addr ON customer_address
BEGIN
  UPDATE customer SET cust_addr=NEW.cust_addr
   WHERE cust_id=NEW.cust_id;
END;

使用上述模式,以下形式的语句

UPDATE customer_address SET cust_addr=$new_address WHERE cust_id=$cust_id;

会导致针对 customer.cust_id 等于 $cust_id 参数的特定客户条目更新 customer.cust_addr 字段。请注意,分配给视图的值如何在触发器主体内的特殊“NEW”表中作为字段提供。

5. 使用 BEFORE 触发器的注意事项

如果 BEFORE UPDATE 或 BEFORE DELETE 触发器修改或删除了要更新或删除的行,则后续更新或删除操作的结果未定义。此外,如果 BEFORE 触发器修改或删除了行,则这些行上本来应该运行的 AFTER 触发器是否会实际运行是未定义的。

在 BEFORE INSERT 触发器中,如果未将 rowid 显式设置为整数,则 NEW.rowid 的值未定义。

由于上述行为,鼓励程序员优先使用 AFTER 触发器而不是 BEFORE 触发器。

6. RAISE() 函数

可以在触发器程序内使用特殊的 SQL 函数 RAISE(),语法如下

引发函数

RAISE ( ROLLBACK , error-message ) IGNORE ABORT FAIL

当在触发器程序执行期间调用 RAISE(ROLLBACK,...)、RAISE(ABORT,...) 或 RAISE(FAIL,...) 之一时,将执行指定的 ON CONFLICT 处理,并且当前查询将终止。错误代码 SQLITE_CONSTRAINT 将与指定的错误消息一起返回给应用程序。

当调用 RAISE(IGNORE) 时,将放弃当前触发器程序的其余部分、导致触发器程序执行的语句以及随后本来会执行的任何后续触发器程序。不会回滚任何数据库更改。如果导致触发器程序执行的语句本身是触发器程序的一部分,则该触发器程序将在下一步的开头恢复执行。

7. 非 TEMP 表上的 TEMP 触发器

触发器通常存在于与 CREATE TRIGGER 语句中“ON”关键字后面的表名称相同的数据库中。但可以对另一个数据库中的表创建 TEMP 触发器。此类触发器仅在定义触发器的应用程序对目标表进行更改时触发。修改数据库的其他应用程序将无法看到 TEMP 触发器,因此无法运行该触发器。

在非 TEMP 表上定义 TEMP 触发器时,务必指定保存非 TEMP 表的数据库。例如,在以下语句中,务必使用“main.tab1”而不是“tab1”

CREATE TEMP TRIGGER ex1 AFTER INSERT ON main.tab1 BEGIN ...

如果未在目标表上指定模式名称,则每当发生任何模式更改时,TEMP 触发器可能会重新附加到另一个数据库中具有相同名称的表。这可能会导致问题。

此页面上次修改于 2024-08-03 15:33:40 UTC