创建触发器

1.语法

创建触发器 stmt:

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

删除-stmt:

表达式:

插入stmt:

选择stmt:

更新stmt:

2.说明

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

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

由于历史疏忽,“UPDATE OF”子句中命名的列实际上不必存在于正在更新的表中。无法识别的列名将被静默忽略。如果“UPDATE OF”子句中的任何名称不是表中的列,那么 SQLite 将使 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 references are valid
UPDATE NEW and OLD references are valid
DELETE OLD references are valid

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

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

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

当触发器关联的表(名表)被 删除时,触发器会自动删除但是,如果触发器操作引用其他表,则在删除或修改其他表时不会删除修改触发器

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

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

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

  • UPDATEDELETEINSERT 语句中要修改的表名必须是非限定表名。换句话说,在指定表时必须只使用“ tablename ”而不是“ database.tablename ”

  • 对于非 TEMP 触发器,要修改或查询的表必须与触发器所附加的表或视图存在于同一数据库中。TEMP 触发器不受相同数据库规则的约束。允许 TEMP 触发器查询或修改任何ATTACH编辑的数据库中的任何表。

  • 不支持 INSERT语句 的“INSERT INTO table DEFAULT VALUES”形式。

  • UPDATEDELETE语句 不支持 INDEXED BY 和 NOT INDEXED 子句

  • 不支持UPDATEDELETE语句 上的 ORDER BY 和 LIMIT 子句。ORDER BY 和 LIMIT 通常不支持UPDATEDELETE在任何上下文中,但可以使用SQLITE_ENABLE_UPDATE_DELETE_LIMIT编译时选项为顶级语句启用。但是,该编译时选项仅适用于顶级UPDATEDELETE 语句,不适用于触发器中的 UPDATEDELETE语句。

  • 触发器内的语句不支持 公用表表达式。

3. INSTEAD OF 触发器

通过在 CREATE TRIGGER 语句中指定 INSTEAD OF,可以在视图和普通表上创建触发器。如果在视图上定义了一个或多个 ON INSERT、ON DELETE 或 ON UPDATE 触发器,则分别在视图上执行 INSERT、DELETE 或 UPDATE 语句不会出错。相反,在视图上执行 INSERT、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”表中的字段提供的。

、使用BEFORE触发器的注意事项

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

NEW.rowid 的值在 BEFORE INSERT 触发器中未定义,其中 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 TRIGGER。这样的触发器只会在定义触发器的应用程序对目标表进行更改时触发。修改数据库的其他应用程序将无法看到 TEMP 触发器,因此无法运行触发器。

在非 TEMP 表上定义 TEMP 触发器时,指定保存非 TEMP 表的数据库很重要。例如,在下面的语句中,重要的是说“main.tab1”而不仅仅是“tab1”:

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

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