CREATE TRIGGER
SQL As Understood By SQLite
[Top]
CREATE TRIGGER
create-trigger-stmt:
隐藏
delete-stmt:
show
qualified-table-name:
show
with-clause:
show
cte-table-name:
show
expr:
show
literal-value:
show
raise-function:
show
type-name:
show
signed-number:
show
insert-stmt:
show
with-clause:
show
cte-table-name:
show
select-stmt:
show
common-table-expression:
show
compound-operator:
show
join-clause:
show
join-constraint:
show
join-operator:
show
ordering-term:
show
result-column:
show
table-or-subquery:
show
update-stmt:
show
column-name-list:
show
qualified-table-name:
show
with-clause:
show
cte-table-name:
show
CREATE TRIGGER语句用于将触发器添加到数据库模式。触发器是在指定的数据库事件发生时自动执行的数据库操作。
可以指定一个触发器,只要发生特定数据库表的DELETE,INSERT或UPDATE,或者在表的一个或多个指定列上发生UPDATE时触发。
此时SQLite仅支持FOR EACH ROW触发器,而不支持FOR EACH STATEMENT触发器。因此明确指定FOR EACH ROW是可选的。FOR EACH ROW意味着触发器中指定的SQL语句可以执行(取决于WHEN子句)每个数据库行被引发触发器的语句插入,更新或删除。
无论是当被插入子句和触发器操作可以访问该行的元素,删除或使用的形式“NEW。的引用更新列名
”和“OLD。列名
”,其中列名
是一个名称与触发器关联的表中的列。OLD和NEW引用只能用于与它们相关的事件的触发器,如下所示:
插 | 新引用是有效的 |
---|---|
UPDATE | NEW和OLD引用是有效的 |
删除 | 旧的参考文件是有效的 |
如果提供WHEN子句,则只有WHEN子句为真时才会执行指定的SQL语句。如果没有提供WHEN子句,则每次触发触发器时都会执行SQL语句。
BEFORE或AFTER关键字决定了何时执行触发器动作相对于相关行的插入,修改或删除。
ON CONFLICT子句可以被指定为触发器主体内UPDATE或INSERT操作的一部分。但是,如果将ON CONFLICT子句指定为引发触发器触发的语句的一部分,则将使用外部语句的冲突处理策略。
当与其关联的表
(表
名表
)被删除时,触发器会自动删除。但是,如果触发器操作引用其他表
,则在删除或修改其他表
时,不会删除或修改触发器。
使用DROP TRIGGER语句删除触发器。
语法限制触发器中的UPDATE,DELETE和INSERT语句
触发器中的UPDATE,DELETE和INSERT语句不支持UPDATE,DELETE和INSERT语句的完整语法。以下限制适用:
- 在UPDATE,DELETE或INSERT语句中要修改的表的名称必须是非限定的
表名
。
换句话说,必须使用只是“表名
”而不是“数据库
。
表名
指定表的时候”。
- 对于非TEMP触发器,要修改或查询的表必须与触发器所附的表或视图位于同一数据库中。TEMP触发器不受同一数据库规则的约束。允许TEMP触发器查询或修改任何ATTACH-ed数据库中的任何表。
- 不支持INSERT语句的“INSERT INTO
table
DEFAULT VALUES”形式。
- UPDATE和DELETE语句不支持INDEXED BY和NOT INDEXED子句。
- 不支持UPDATE和DELETE语句上的ORDER BY和LIMIT子句。在任何情况下,ORDER BY和LIMIT通常不支持UPDATE或DELETE,但可以使用SQLITE_ENABLE_UPDATE_DELETE_LIMIT编译时选项为顶级语句启用。但是,该编译时选项仅适用于顶级UPDATE和DELETE语句,而不适用于触发器中的UPDATE和DELETE语句。
- 公用表表达式不支持触发器内部的语句。
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编译器会计数INSTEAD OF触发器触发。
Some Example Triggers
假设客户记录存储在“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_addr字段针对customer.cust_id等于$ cust_id参数的特定客户条目进行更新。请注意,如何将分配给视图的值作为触发器主体内特殊“NEW”表中的字段提供。
关于使用BEFORE触发器的注意事项
如果BEFORE UPDATE或BEFORE DELETE触发器修改或删除了要更新或删除的行,则后续更新或删除操作的结果是未定义的。此外,如果一个BEFORE触发器修改或删除一行,那么它是未定义的,否则在这些行上运行的AFTER触发器事实上将运行。
NEW.rowid的值在BEFORE INSERT触发器中未定义,其中rowid未显式设置为整数。
由于上述行为,鼓励程序员比BEFORE触发器更喜欢AFTER触发器。
The RAISE() function
可以在触发器程序中使用特殊的SQL函数RAISE(),其语法如下
raise-function:
当在触发程序执行期间调用RAISE(ROLLBACK,...),RAISE(ABORT,...)或RAISE(FAIL,...)之一时,执行指定的ON CONFLICT处理,当前查询终止。将SQLITE_CONSTRAINT的错误代码与指定的错误消息一起返回给应用程序。
当调用RAISE(IGNORE)时,将放弃当前触发程序的其余部分,导致触发程序执行的语句以及任何后续执行的触发程序。没有数据库更改回滚。如果导致触发程序执行的语句本身是触发程序的一部分,那么该触发程序将在下一步开始时恢复执行。
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触发器在发生任何模式更改时重新连接到另一个数据库中具有相同名称的表。