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:
节目
WITH
RECURSIVE
common-table-expression
,
DELETE
FROM
qualified-table-name
returning-clause
expr
WHERE
公用表表达式:
节目
table-name
(
column-name
)
AS
NOT
MATERIALIZED
(
select-stmt
)
,
限定表名:
节目
schema-name
.
table-name
AS
alias
INDEXED
BY
index-name
NOT
INDEXED
返回子句:
节目
RETURNING
expr
AS
column-alias
*
,
表达式:
节目
literal-value
bind-parameter
schema-name
.
table-name
.
column-name
unary-operator
expr
expr
binary-operator
expr
function-name
(
DISTINCT
expr
)
filter-clause
over-clause
,
*
(
expr
)
,
CAST
(
expr
AS
type-name
)
expr
COLLATE
collation-name
expr
NOT
LIKE
GLOB
REGEXP
MATCH
expr
expr
ESCAPE
expr
expr
ISNULL
NOTNULL
NOT
NULL
expr
IS
NOT
DISTINCT
FROM
expr
expr
NOT
BETWEEN
expr
AND
expr
expr
NOT
IN
(
select-stmt
)
expr
,
schema-name
.
table-function
(
expr
)
table-name
,
NOT
EXISTS
(
select-stmt
)
CASE
expr
WHEN
expr
THEN
expr
ELSE
expr
END
raise-function
过滤器子句:
节目
字面值:
节目
CURRENT_TIMESTAMP
numeric-literal
string-literal
blob-literal
NULL
TRUE
FALSE
CURRENT_TIME
CURRENT_DATE
过度条款:
节目
OVER
window-name
(
base-window-name
PARTITION
BY
expr
,
ORDER
BY
ordering-term
,
frame-spec
)
框架规格:
节目
GROUPS
BETWEEN
UNBOUNDED
PRECEDING
AND
UNBOUNDED
FOLLOWING
RANGE
ROWS
UNBOUNDED
PRECEDING
expr
PRECEDING
CURRENT
ROW
expr
PRECEDING
CURRENT
ROW
expr
FOLLOWING
expr
PRECEDING
CURRENT
ROW
expr
FOLLOWING
EXCLUDE
CURRENT
ROW
EXCLUDE
GROUP
EXCLUDE
TIES
EXCLUDE
NO
OTHERS
订购条款:
节目
expr
COLLATE
collation-name
DESC
ASC
NULLS
FIRST
NULLS
LAST
提升功能:
节目
RAISE
(
ROLLBACK
,
error-message
)
IGNORE
ABORT
FAIL
类型名称:
节目
name
(
signed-number
,
signed-number
)
(
signed-number
)
签名号码:
节目
插入stmt:
节目
WITH
RECURSIVE
common-table-expression
,
REPLACE
INSERT
OR
ROLLBACK
INTO
ABORT
FAIL
IGNORE
REPLACE
schema-name
.
table-name
AS
alias
(
column-name
)
,
VALUES
(
expr
)
,
,
upsert-clause
select-stmt
upsert-clause
DEFAULT
VALUES
returning-clause
公用表表达式:
节目
table-name
(
column-name
)
AS
NOT
MATERIALIZED
(
select-stmt
)
,
返回子句:
节目
RETURNING
expr
AS
column-alias
*
,
更新子句:
节目
ON
CONFLICT
(
indexed-column
)
WHERE
expr
DO
,
conflict target
UPDATE
SET
column-name-list
=
expr
WHERE
expr
NOTHING
,
column-name
列名列表:
节目
索引列:
节目
column-name
COLLATE
collation-name
DESC
expr
ASC
选择stmt:
节目
WITH
RECURSIVE
common-table-expression
,
SELECT
DISTINCT
result-column
,
ALL
FROM
table-or-subquery
join-clause
,
WHERE
expr
GROUP
BY
expr
HAVING
expr
,
WINDOW
window-name
AS
window-defn
,
VALUES
(
expr
)
,
,
compound-operator
select-core
ORDER
BY
LIMIT
expr
ordering-term
,
OFFSET
expr
,
expr
公用表表达式:
节目
table-name
(
column-name
)
AS
NOT
MATERIALIZED
(
select-stmt
)
,
复合运算符:
节目
UNION
UNION
INTERSECT
EXCEPT
ALL
加入子句:
节目
table-or-subquery
join-operator
table-or-subquery
join-constraint
加入约束:
节目
USING
(
column-name
)
,
ON
expr
联合运营商:
节目
NATURAL
LEFT
OUTER
JOIN
,
RIGHT
FULL
INNER
CROSS
订购条款:
节目
expr
COLLATE
collation-name
DESC
ASC
NULLS
FIRST
NULLS
LAST
结果列:
节目
expr
AS
column-alias
*
table-name
.
*
表或子查询:
节目
schema-name
.
table-name
AS
table-alias
INDEXED
BY
index-name
NOT
INDEXED
table-function-name
(
expr
)
,
AS
table-alias
(
select-stmt
)
(
table-or-subquery
)
,
join-clause
窗口定义:
节目
(
base-window-name
PARTITION
BY
expr
,
ORDER
BY
ordering-term
,
frame-spec
)
框架规格:
节目
GROUPS
BETWEEN
UNBOUNDED
PRECEDING
AND
UNBOUNDED
FOLLOWING
RANGE
ROWS
UNBOUNDED
PRECEDING
expr
PRECEDING
CURRENT
ROW
expr
PRECEDING
CURRENT
ROW
expr
FOLLOWING
expr
PRECEDING
CURRENT
ROW
expr
FOLLOWING
EXCLUDE
CURRENT
ROW
EXCLUDE
GROUP
EXCLUDE
TIES
EXCLUDE
NO
OTHERS
更新stmt:
节目
WITH
RECURSIVE
common-table-expression
,
UPDATE
OR
ROLLBACK
qualified-table-name
OR
REPLACE
OR
IGNORE
OR
FAIL
OR
ABORT
SET
column-name-list
=
expr
column-name
,
FROM
table-or-subquery
,
join-clause
WHERE
expr
returning-clause
列名列表:
节目
公用表表达式:
节目
table-name
(
column-name
)
AS
NOT
MATERIALIZED
(
select-stmt
)
,
加入子句:
节目
table-or-subquery
join-operator
table-or-subquery
join-constraint
加入约束:
节目
USING
(
column-name
)
,
ON
expr
联合运营商:
节目
NATURAL
LEFT
OUTER
JOIN
,
RIGHT
FULL
INNER
CROSS
限定表名:
节目
schema-name
.
table-name
AS
alias
INDEXED
BY
index-name
NOT
INDEXED
返回子句:
节目
RETURNING
expr
AS
column-alias
*
,
表或子查询:
节目
schema-name
.
table-name
AS
table-alias
INDEXED
BY
index-name
NOT
INDEXED
table-function-name
(
expr
)
,
AS
table-alias
(
select-stmt
)
(
table-or-subquery
)
,
join-clause
2. 说明
CREATE TRIGGER 语句用于将触发器添加到数据库模式。 触发器是在指定的数据库事件发生时自动执行的数据库操作。
每个触发器都必须指定它将针对以下操作之一触发: DELETE 、 INSERT 、 UPDATE 。 对于删除、插入或更新的每一行,触发器都会触发一次。 如果使用“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 子句可以指定为
触发器主体内的 UPDATE 或 INSERT 操作的一部分。 但是,如果将 ON CONFLICT 子句指定为导致触发器触发的语句的一部分,则使用外部语句的冲突处理策略。
当触发器关联的表( 表 名表)被
删除 时,触发器会自动 删除 。 但是,如果触发器操作引用其他表,则在删除或修改其他表时不会删除 或 修改 触发器 。
使用 DROP TRIGGER 语句删除触发器。
2.1. 触发器中 UPDATE、DELETE 和 INSERT 语句的语法限制
触发器中的 UPDATE 、 DELETE 和 INSERT
语句不支持 UPDATE 、 DELETE 和 INSERT 语句的完整语法。 以下限制适用:
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 触发器在发生任何模式更改时重新附加到另一个数据库中具有相同名称的表。