SQL Server中帶有OUTPUT子句的INSERT,DELETE,UPDATE應用
OUTPUT是SQL SERVER2005的新特性,可以從數據修改語句中返回輸出,可以看作是"返回結果的DML"。
INSERT、DELETE、UPDATE均支持OUTPUT子句。
在OUTPUT子句中,可以引用特殊表inserted和deleted,使用inserted和deleted表與在觸發器中使用的非常相似。
在INSERT,DELETE,UPDATE中OUTPUT的區別
- 對於INSERT,可以引用inserted表以查詢新行的屬性。
- 對於DELETE,可以引用deleted表以查詢舊行的屬性。
- 對於UPDATE,使用deleted表查詢被更新行在更改前的屬性,用inserted表標識被更新行在更改後的值。
輸出方式:
- 輸出給調用方(客戶端應用程序)
- 輸出給表
一、應用:
1、帶有OUTPUT的INSERT的應用
對於包含自增列的表執行多行insert語句,同時想知道新的標識值時,在INSERT中使用OUTPUT子句非常方便。
1、對於單行INSERT語句,這不成問題:SCOPE_IDENTITY函數即可實現。SCOPE_IDENTITY函數:返回為當前會話和當前作用域中的任何表最後生成的標識值。
-- Generating Surrogate Keys for Customers USE tempdb; GO IF OBJECT_ID('dbo.CustomersDim') IS NOT NULL DROP TABLE dbo.CustomersDim; GO CREATE TABLE dbo.CustomersDim ( KeyCol INT NOT NULL IDENTITY PRIMARY KEY, CustomerID NCHAR(5) NOT NULL, CompanyName NVARCHAR(40) NOT NULL, ); -- Insert New Customers and Get their Surrogate Keys DECLARE @NewCusts TABLE ( CustomerID NCHAR(5) NOT NULL PRIMARY KEY, KeyCol INT NOT NULL UNIQUE ); INSERT INTO dbo.CustomersDim(CustomerID, CompanyName) OUTPUT inserted.CustomerID, inserted.KeyCol INTO @NewCusts -- OUTPUT inserted.CustomerID, inserted.KeyCol SELECT CustomerID, CompanyName FROM Northwind.dbo.Customers WHERE Country = N'UK'; SELECT CustomerID, KeyCol FROM @NewCusts; GO
註意代碼中被註釋掉的第二個OUTPUT子句,後面沒有INTO子句。如果還要輸出返回給調用方,取消註釋即可。這樣INSERT語句將包含兩個OUTPUT子句。
2、多行INSERT語句
USE AdventureWorks; GO CREATE TABLE TestTable (ID INT, TEXTVal VARCHAR(100)) DECLARE @TmpTable TABLE (ID INT, TEXTVal VARCHAR(100)) INSERT TestTable (ID, TEXTVal) OUTPUT Inserted.ID, Inserted.TEXTVal INTO @TmpTable VALUES (1,'FirstVal') INSERT TestTable (ID, TEXTVal) OUTPUT Inserted.ID, Inserted.TEXTVal INTO @TmpTable VALUES (2,'SecondVal') SELECT * FROM @TmpTable SELECT * FROM TestTable DROP TABLE TestTable GO
2、帶有OUTPUT的DELETE的應用.
如果要刪除數據的同時,還需要記錄日志,或者歸檔數據,在DELETE中使用OUTPUT子句在適合不過瞭。
USE AdventureWorks; GO CREATE TABLE TestTable (ID INT, TEXTVal VARCHAR(100)) DECLARE @TmpTable TABLE (ID INT, TEXTVal VARCHAR(100)) INSERT TestTable (ID, TEXTVal) VALUES (1,'FirstVal') INSERT TestTable (ID, TEXTVal) VALUES (2,'SecondVal') DELETE FROM TestTable OUTPUT Deleted.ID, Deleted.TEXTVal INTO @TmpTable WHERE ID IN (1,2) SELECT * FROM @TmpTable SELECT * FROM TestTable DROP TABLE TestTable GO
3、帶有OUTPUT的UPDATE的應用
USE AdventureWorks; GO CREATE TABLE TestTable (ID INT, TEXTVal VARCHAR(100)) DECLARE @TmpTable TABLE (ID_New INT, TEXTVal_New VARCHAR(100),ID_Old INT, TEXTVal_Old VARCHAR(100)) INSERT TestTable (ID, TEXTVal) VALUES (1,'FirstVal') INSERT TestTable (ID, TEXTVal) VALUES (2,'SecondVal') UPDATE TestTable SET TEXTVal = 'NewValue' OUTPUT Inserted.ID, Inserted.TEXTVal, Deleted.ID, Deleted.TEXTVal INTO @TmpTable WHERE ID IN (1,2) SELECT * FROM @TmpTable SELECT * FROM TestTable DROP TABLE TestTable GO
4、在 UPDATE 語句中使用包含 from_table_name 的 OUTPUT INTO
以下示例使用指定的 ProductID
和 ScrapReasonID
,針對 WorkOrder
表中的所有工作順序更新 ScrapReasonID
列。
OUTPUT INTO
子句返回所更新表 (WorkOrder
) 中的值以及 Product
表中的值。 在 Product
子句中使用 FROM
表來指定要更新的行。
由於 WorkOrder
表上定義瞭 AFTER UPDATE
觸發器,因此需要 INTO
關鍵字。
USE AdventureWorks2012; GO DECLARE @MyTestVar TABLE ( OldScrapReasonID INT NOT NULL, NewScrapReasonID INT NOT NULL, WorkOrderID INT NOT NULL, ProductID INT NOT NULL, ProductName NVARCHAR(50)NOT NULL); UPDATE Production.WorkOrder SET ScrapReasonID = 4 OUTPUT deleted.ScrapReasonID, inserted.ScrapReasonID, inserted.WorkOrderID, inserted.ProductID, p.Name INTO @MyTestVar FROM Production.WorkOrder AS wo INNER JOIN Production.Product AS p ON wo.ProductID = p.ProductID AND wo.ScrapReasonID= 16 AND p.ProductID = 733; SELECT OldScrapReasonID, NewScrapReasonID, WorkOrderID, ProductID, ProductName FROM @MyTestVar; GO
4、MERGE語句
下面的示例捕獲從 OUTPUT
語句的 MERGE
子句返回的數據,並將這些數據插入另一個表。
MERGE
語句每天根據在 Quantity
表中處理的訂單更新 ProductInventory
表的 SalesOrderDetail
列。 如果產品的庫存降至 0
或更低,它還會刪除與這些產品對應的行。
本示例捕獲已刪除的行並將這些行插入另一個表 ZeroInventory
中,該表跟蹤沒有庫存的產品。
USE AdventureWorks2012; GO IF OBJECT_ID(N'Production.ZeroInventory', N'U') IS NOT NULL DROP TABLE Production.ZeroInventory; GO --Create ZeroInventory table. CREATE TABLE Production.ZeroInventory (DeletedProductID int, RemovedOnDate DateTime); GO INSERT INTO Production.ZeroInventory (DeletedProductID, RemovedOnDate) SELECT ProductID, GETDATE() FROM ( MERGE Production.ProductInventory AS pi USING (SELECT ProductID, SUM(OrderQty) FROM Sales.SalesOrderDetail AS sod JOIN Sales.SalesOrderHeader AS soh ON sod.SalesOrderID = soh.SalesOrderID AND soh.OrderDate = '20070401' GROUP BY ProductID) AS src (ProductID, OrderQty) ON (pi.ProductID = src.ProductID) WHEN MATCHED AND pi.Quantity - src.OrderQty <= 0 THEN DELETE WHEN MATCHED THEN UPDATE SET pi.Quantity = pi.Quantity - src.OrderQty OUTPUT $action, deleted.ProductID) AS Changes (Action, ProductID) WHERE Action = 'DELETE'; IF @@ROWCOUNT = 0 PRINT 'Warning: No rows were inserted'; GO SELECT DeletedProductID, RemovedOnDate FROM Production.ZeroInventory;
二、使用OUTPUT子句的註意事項:
以下語句中不支持 OUTPUT 子句:
- 引用本地分區視圖、分佈式分區視圖或遠程表的 DML 語句。
- 包含 EXECUTE 語句的 INSERT 語句。
- 不能將 OUTPUT INTO 子句插入視圖或行集函數。
- 參數或變量作為 UPDATE 語句的一部分進行瞭修改,則 OUTPUT 子句將始終返回語句執行之前的參數或變量的值而不是已修改的值
三、C#中使用cmd.ExecuteScalar(單列)、cmdExecuteReader(多行或多列)
返回單列:
using(SqlCommand cmd=new SqlCommand("INSERT INTO Mem_Basic(Mem_Na,Mem_Occ) output INSERTED.ID VALUES(@na,@occ)",con)) { cmd.Parameters.AddWithValue("@na", Mem_NA); cmd.Parameters.AddWithValue("@occ", Mem_Occ); con.Open(); int modified =(int)cmd.ExecuteScalar(); if (con.State == System.Data.ConnectionState.Open) con.Close(); return modified; }
返回多行或者多列:
create table Suspension (pkey int not null identity(1, 1), pallet_position int, processing_pallet_pkey int, datetime_created datetime, datetime_updated datetime, [this.created_by] int, [this.updated_by] int); using (var conn = new SqlConnection(connectionString)) { conn.Open(); const string insertQuery = @" INSERT INTO dbo.Suspension (pallet_position, processing_pallet_pkey, datetime_created, datetime_updated, [this.created_by], [this.updated_by]) OUTPUT INSERTED.pkey VALUES (1, 2, '20141013 16:27:25.000', '20141013 16:27:25.000', 2, 2), (2, 2, '20141013 16:27:25.000', '20141013 16:27:25.000', 2, 2), (3, 2, '20141013 16:27:25.000', '20141013 16:27:25.000', 2, 2), (4, 2, '20141013 16:27:25.000', '20141013 16:27:25.000', 2, 2);"; // 通過數據庫 DataTable dt = new DataTable(); using (SqlCommand cmd = new SqlCommand(insertQuery, conn)) using (var insertedOutput = cmd.ExecuteReader()) { dt.Load(insertedOutput); } Console.WriteLine(dt.Rows.Count); // 4 // 通過手工讀取 var list = new List<int>(); using (SqlCommand cmd = new SqlCommand(insertQuery, conn)) using (var insertedOutput = cmd.ExecuteReader()) { while(insertedOutput.Read()) { list.Add(insertedOutput.GetInt32(0)); } } Console.WriteLine(list.Count); // 4 // 通過dapper var ids = conn.Query<int>(insertQuery).ToList(); Console.WriteLine(ids.Count); // 4 }
四、參考:
https://docs.microsoft.com/zh-cn/sql/t-sql/queries/output-clause-transact-sql?view=sql-server-ver15&viewFallbackFrom=sql-server-2014
以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。
推薦閱讀:
- SQL Server 觸發器詳情
- SQL Server的觸發器詳解
- MySQL中增刪改查操作與常見陷阱詳解
- mysql ON DUPLICATE KEY UPDATE重復插入時更新方式
- MySQL教程DML數據操縱語言示例詳解