在DELETE触发器中,被删除的行存于只读的DELETED虚拟表中,结构与原表一致,仅限触发器作用域内可用,须通过SELECT直接访问。

DELETE 触发器里怎么拿到被删的行
在 DELETE 触发器中,被删除的记录不会消失——它被临时存放在 DELETED 伪表里。这是 SQL Server 特有的机制(MySQL / PostgreSQL 不同),所以第一步先确认你用的是 SQL Server;其他数据库得换思路。
常见错误是直接查原表,比如写 SELECT * FROM orders WHERE id = @id,但此时数据已从原表移除,查不到。必须从 DELETED 表取。
-
DELETED是只读的内存表,结构和原表一致,每行对应一条被删记录 - 对单行
DELETE,DELETED有一行;批量删(如WHERE status = 'canceled'),DELETED可能有多行 - 不能对
DELETED执行INSERT、UPDATE或DELETE,只能SELECT
SQL Server DELETE 触发器写法示例
假设有个 orders 表,想在删订单时把客户ID和金额记进日志表:
CREATE TRIGGER tr_log_deleted_orders
ON orders
AFTER DELETE
AS
BEGIN
INSERT INTO order_deletion_log (customer_id, amount, deleted_at)
SELECT customer_id, amount, GETDATE()
FROM DELETED;
END;注意这里没加 WHERE 条件——因为 DELETED 已经只含本次被删的行,直接 SELECT 即可。如果只想记录特定状态的删除(比如只记“已支付”订单被删),可以在 SELECT 后加 WHERE 过滤 DELETED 中的字段。
为什么不能用 INSTEAD OF DELETE 替代?
INSTEAD OF DELETE 确实也能访问 DELETED,但它会**阻止原 DELETE 操作执行**,需要你手动写 DELETE FROM ... 才真删数据。多数场景不需要干预删除逻辑,只做审计或归档,用 AFTER DELETE 更安全、更直观。
- 误用
INSTEAD OF可能导致数据没真正删掉,表面看成功,实际残留 -
AFTER DELETE中的DELETED内容和最终删掉的数据完全一致;而INSTEAD OF里若逻辑出错,DELETED和实际结果可能不匹配 - 触发器嵌套时,
AFTER类型默认不递归触发(除非显式开启),更可控
跨数据库差异:MySQL 和 PostgreSQL 怎么办
MySQL 没有 DELETED 表,但可以在 BEFORE DELETE 触发器里通过 OLD.列名 访问即将删除的值,例如:INSERT INTO log VALUES(OLD.id, OLD.amount)。注意必须是 BEFORE,AFTER 里 OLD 不可用。
PostgreSQL 用 OLD 关键字,同样只在 BEFORE DELETE 或 INSTEAD OF DELETE 中有效,语法类似:INSERT INTO log SELECT OLD.id, OLD.amount;。它的 OLD 是行变量,不是表,所以不能 JOIN 或 GROUP BY,批量删时需用 FOR EACH ROW 触发器逐行处理。
跨库移植触发器时,DELETED / OLD / NEW 的存在性、作用域和使用时机是第一个要核对的点。

















