許多開發者以為 SQL Server 觸發器只能逐行觸發,實際上 inserted 與 deleted 虛擬表包含**該語句影響的所有行**。只需正確迭代即可實現真正的集合級觸發邏輯。
CREATE TABLE tbl_src (id INT PRIMARY KEY, val VARCHAR(100));
CREATE TABLE tbl_hist (id INT PRIMARY KEY, val VARCHAR(100), del_dt DATETIME DEFAULT GETDATE());
DECLARE @i INT = 1;
WHILE @i <= 100
BEGIN
INSERT INTO tbl_src (id, val) VALUES (@i, CAST(@i AS VARCHAR));
SET @i += 1;
END
CREATE TRIGGER trg_del_archive ON tbl_src
FOR DELETE
AS
BEGIN
DECLARE @id INT, @val VARCHAR(100);
DECLARE cur_del CURSOR LOCAL FORWARD_ONLY FOR
SELECT id, val FROM deleted; -- 包含所有被刪除的行
OPEN cur_del;
FETCH NEXT FROM cur_del INTO @id, @val;
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO tbl_hist (id, val) VALUES (@id, @val);
FETCH NEXT FROM cur_del INTO @id, @val;
END
CLOSE cur_del;
DEALLOCATE cur_del;
END;
DELETE FROM tbl_src WHERE id BETWEEN 10 AND 20; -- 刪除 11 行
SELECT * FROM tbl_hist; -- 11 筆歷史記錄完整保留
inserted / deleted 是**結果集**,非單行;可直接 SELECT ... FROM inserted 做集合操作 (如 INSERT INTO hist SELECT * FROM deleted),**完全不需要游標**。INSERT INTO tbl_hist (id, val) SELECT id, val FROM deleted;INSTEAD OF 觸發器同樣可存取 inserted/deleted,邏輯一致。CREATE TRIGGER trg_name ON tbl
AFTER INSERT, UPDATE, DELETE
AS
BEGIN
SET NOCOUNT ON;
-- 1. 早期退出:無影響行直接返回
IF NOT EXISTS (SELECT 1 FROM inserted) AND NOT EXISTS (SELECT 1 FROM deleted)
RETURN;
-- 2. 集合式業務邏輯
INSERT INTO audit_log (tbl_name, op_type, pk_val, mod_dt)
SELECT 'tbl',
CASE WHEN EXISTS(SELECT 1 FROM inserted) AND EXISTS(SELECT 1 FROM deleted) THEN 'U'
WHEN EXISTS(SELECT 1 FROM inserted) THEN 'I'
ELSE 'D' END,
COALESCE(i.pk, d.pk),
GETDATE()
FROM inserted i
FULL JOIN deleted d ON i.pk = d.pk;
END;
SQL Server 觸發器**原生支援多行**,關鍵在於正確理解 inserted/deleted 為多行集合。優先使用集合操作,僅在極少數逐行不可避免場景才引入游標,即能寫出高效、健壯的觸發器。