適用於:SQL Server
Azure SQL Database
Azure SQL 受控執行個體
Microsoft Fabric 中的 SQL 資料庫
DML 觸發程序陳述式會使用兩個特殊資料表:名為「deleted」和「inserted」的資料表。 SQL Server 會自動建立並管理這些資料表。 您可以使用這些暫存、常駐記憶體的資料表來測試某些資料修改的效果,以及設定 DML 觸發程序動作的條件。 你無法直接修改資料表中的資料,也無法對資料表執行資料定義語言(DDL)操作,例如 CREATE INDEX。
了解已插入與已刪除的資料表
在 DML 觸發器中,inserted 和 deleted 資料表主要是用來執行下列動作:
擴充資料表之間的參考完整性。
在檢視底下的基底資料表中插入或更新資料。
測試錯誤,並根據錯誤採取動作。
尋找資料修改前後之資料表狀態的差異,並依據這些差異採取動作。
已刪除的資料表會將受影響列的副本存於觸發表中,這些資料列在被 DELETE or UPDATE 語句更改之前(觸發表是 DML 觸發器執行的表)。 在執行 DELETE or UPDATE 語句時,受影響的列會先從觸發表複製並轉移到已刪除的資料表。
插入的表格會儲存在 or INSERT 語句後UPDATE的新或變更列的副本。 在執行 INSERT or UPDATE 語句時,觸發表中新增或變更的列會被複製到插入的表中。 在插入的資料表中,資料列是觸發程序資料表中新增加或更新的資料列的複本。
更新交易類似於先執行刪除作業,再執行插入作業。 在執行語 UPDATE 句時,會發生以下事件序列:
- 原始資料列從觸發器資料表複製到 "deleted" 資料表。
- 觸發表會更新到來自該 UPDATE 語句的新值。
- 觸發程式資料表中更新的資料列被複製到已插入資料表。
您可以藉此比較更新前的資料列內容 (在 deleted 資料表中) 與更新後的新資料列值 (在 inserted 資料表中)。
當您設定觸發程序條件時,應適當使用 inserted 及 deleted 資料表來配合觸發程式的執行。 雖然在測試 a INSERT 時參考已刪除的表格或在測試 a DELETE 時引用插入的表格不會造成錯誤,但這些觸發測試表格在這些情況下並未包含任何列。
注意
如果觸發動作取決於資料修改所影響的列數,請對多列資料修改ROWCOUNT(如 、 、 INSERT或DELETE基於 SELECT 陳述式)進行測試(例如 @@UPDATE 的檢查),並採取適當的行動。 如需詳細資訊,請參閱 建立 DML 觸發程序以處理多重資料列。
SQL Server 不允許在 AFTER 觸發程序中,對 inserted 和 deleted 資料表中的 text、ntext 或 image 資料行進行引用。 然而,這些資料類型的包含僅是為了向後相容性。 大型資料的慣用儲存體應使用 varchar(max) 、 nvarchar(max) ,以及 varbinary(max) 資料類型。 AFTER 和 INSTEAD OF 兩個觸發程序都支援已插入及已刪除資料表中的 varchar(max) 、 nvarchar(max) ,和 varbinary(max) 資料。 欲了解更多資訊,請參見 CREATE TRIGGER (Transact-SQL)。
範例:在觸發程序中使用插入的資料表來實施業務規則
由於 CHECK 條件限制只能參考定義在欄位層級或表格層級的資料行,因此任何跨資料表的條件限制(在此為商業規則)都必須定義為觸發器。
下列範例會建立一個 DML 觸發器。 當試圖在 PurchaseOrderHeader 資料表中插入新的採購單時,這個觸發程序會檢查確認供應商的信用等級良好。 若要取得與剛插入的採購單對應的供應商信用評等,您必須參考 Vendor 資料表,並與 inserted 資料表聯結。 如果信用等級太低,將會顯示訊息,且不會執行插入動作。
USE AdventureWorks2022;
GO
IF OBJECT_ID('Purchasing.LowCredit', 'TR') IS NOT NULL
DROP TRIGGER Purchasing.LowCredit;
GO
-- This trigger prevents a row from being inserted in the Purchasing.PurchaseOrderHeader table
-- when the credit rating of the specified vendor is set to 5 (below average).
CREATE TRIGGER Purchasing.LowCredit
ON Purchasing.PurchaseOrderHeader
AFTER INSERT
AS
IF (ROWCOUNT_BIG() = 0)
RETURN;
IF EXISTS (SELECT 1
FROM inserted AS i
INNER JOIN Purchasing.Vendor AS v
ON v.BusinessEntityID = i.VendorID
WHERE v.CreditRating = 5)
BEGIN
RAISERROR ('A vendor''s credit rating is too low to accept new purchase orders.', 16, 1);
ROLLBACK;
RETURN;
END
GO
-- This statement attempts to insert a row into the PurchaseOrderHeader table
-- for a vendor that has a below average credit rating.
-- The AFTER INSERT trigger is fired and the INSERT transaction is rolled back.
INSERT INTO Purchasing.PurchaseOrderHeader (RevisionNumber, Status, EmployeeID,
VendorID, ShipMethodID, OrderDate, ShipDate, SubTotal, TaxAmt, Freight)
VALUES (2, 3, 261, 1652, 4, GETDATE(), GETDATE(), 44594.55, 3567.564, 1114.8638);
GO
在 INSTEAD OF 觸發程序中使用 inserted 與 deleted 資料表
傳遞到依據資料表定義的 INSTEAD OF 觸發程序之 inserted 及 deleted 資料表,會與傳遞到 AFTER 觸發程序的 inserted 及 deleted 資料表遵循相同的規則。 插入和刪除資料表的格式與定義了 INSTEAD OF 觸發程序的資料表格式相同。 inserted 及 deleted 資料表中的每一資料行,都會直接對應到基底資料表中的資料行。
以下關於何時 INSERT 引用帶有 INSTEAD OF 觸發條件的資料表的 OR UPDATE 語句必須為欄位提供值的規則,與沒有 INSTEAD OF 觸發器時相同:
無法為計算資料行或資料類型為 timestamp 的資料行指定值。
對於具有屬性 IDENTITY 的欄位,除非 IDENTITY_INSERT 該資料表為 ON ,否則無法指定值。 當 IDENTITY_INSERT 是 開啟時, INSERT 語句必須提供一個值。
INSERT 語句必須提供所有沒有 DEFAULT 限制的 NOT NULL 欄位的值。
對於除計算欄、身份欄或 時間戳欄 外,任何允許 null 的欄位或任何有 DEFAULT 定義的 NOT NULL 欄位,值皆可選。
當 INSERT、 UPDATE或 DELETE 陳述句引用具有 INSTEAD OF 觸發器的視圖時,資料庫引擎 會呼叫該觸發器,而非對任何資料表採取直接行動。 觸發程序必須使用 inserted 及 deleted 資料表中的資訊,來建立實作基底資料表中要求的動作所需的陳述式,即使為檢視所建立的 inserted 及 deleted 資料表中的資訊格式與基底資料表中的資料格式並不相同也一樣。
傳遞給定義於檢視上的 INSTEAD OF 觸發器的 inserted 和 deleted 資料表的格式,必須與為該檢視定義的 SELECT 陳述式的選取清單相匹配。 例如:
USE AdventureWorks2022;
GO
CREATE VIEW dbo.EmployeeNames (BusinessEntityID, LName, FName)
AS
SELECT e.BusinessEntityID, p.LastName, p.FirstName
FROM HumanResources.Employee AS e
JOIN Person.Person AS p
ON e.BusinessEntityID = p.BusinessEntityID;
此檢視設定的結果有三個資料行:一個 int 資料行與兩個 nvarchar 資料行。 傳遞至檢視所定義的 INSTEAD OF 觸發程序的已插入或已刪除資料表,包含一個名為 的 BusinessEntityID 欄位、一個名為 的 LName 欄位,以及一個名為 的 FName 欄位。
檢視的選取清單也可以包含不直接對應到單一基礎表格欄位的運算式。 有些運算式,例如常數或函數調用,可能不參考任何資料行且可以被忽略。 複雜運算式可以參考多個資料行,但 inserted 及 deleted 資料表的每個插入資料列只能有一個值。 檢視中的簡單運算式若參考到具有複雜運算式的計算資料行,也會遇到相同的問題。 檢視的觸發程序 (INSTEAD OF) 必須處理這些類型的運算式。
效能考量
因為 inserted 與 deleted 資料表是虛擬的記憶體駐留資料表,所以無法使用統計資料或索引等屬性。 雖然這些資料表的部分基數資訊會公開,您還是務必要審慎思考暫時儲存在該處的資料列數目。 在這些數據表中插入大量數據列,並查詢或聯結其他數據表可能會導致次佳查詢計劃和查詢執行速度變慢。 請務必仔細設計和測試應用程式,以滿足您的查詢效能需求。