• 设为首页
  • 收藏本站
  • 积分充值
  • VIP赞助
  • 手机版
  • 微博
  • 微信
    微信公众号 添加方式:
    1:搜索微信号(888888
    2:扫描左侧二维码
  • 快捷导航
    福建二哥 门户 查看主题

    SQL Server中带有OUTPUT子句的INSERT,DELETE,UPDATE应用

    发布者: 404号房间 | 发布时间: 2025-6-18 14:12| 查看数: 107| 评论数: 0|帖子模式

    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函数:返回为当前会话和当前作用域中的任何表最后生成的标识值。
    1. -- Generating Surrogate Keys for Customers  
    2. USE tempdb;  
    3. GO  
    4. IF OBJECT_ID('dbo.CustomersDim') IS NOT NULL  
    5.   DROP TABLE dbo.CustomersDim;  
    6. GO  

    7. CREATE TABLE dbo.CustomersDim  
    8. (  
    9.   KeyCol      INT          NOT NULL IDENTITY PRIMARY KEY,  
    10.   CustomerID  NCHAR(5)     NOT NULL,  
    11.   CompanyName NVARCHAR(40) NOT NULL,  

    12. );  

    13. -- Insert New Customers and Get their Surrogate Keys  
    14. DECLARE @NewCusts TABLE  
    15. (  
    16.   CustomerID NCHAR(5) NOT NULL PRIMARY KEY,  
    17.   KeyCol     INT      NOT NULL UNIQUE  
    18. );  

    19. INSERT INTO dbo.CustomersDim(CustomerID, CompanyName)  
    20.        OUTPUT inserted.CustomerID, inserted.KeyCol    INTO @NewCusts  
    21.     -- OUTPUT inserted.CustomerID, inserted.KeyCol  
    22.   SELECT CustomerID, CompanyName  FROM Northwind.dbo.Customers    WHERE Country = N'UK';  

    23. SELECT CustomerID, KeyCol FROM @NewCusts;  
    24. GO
    复制代码
    注意代码中被注释掉的第二个OUTPUT子句,后面没有INTO子句。如果还要输出返回给调用方,取消注释即可。这样INSERT语句将包含两个OUTPUT子句。
    2、多行INSERT语句
    1. USE AdventureWorks;  
    2. GO

    3. CREATE TABLE TestTable (ID INT, TEXTVal VARCHAR(100))  

    4. DECLARE @TmpTable TABLE (ID INT, TEXTVal VARCHAR(100))  

    5. INSERT TestTable (ID, TEXTVal)  
    6.     OUTPUT Inserted.ID, Inserted.TEXTVal INTO @TmpTable  
    7. VALUES (1,'FirstVal')  
    8. INSERT TestTable (ID, TEXTVal)  
    9.      OUTPUT Inserted.ID, Inserted.TEXTVal INTO @TmpTable  
    10. VALUES (2,'SecondVal')  

    11. SELECT * FROM @TmpTable  
    12. SELECT * FROM TestTable  

    13. DROP TABLE TestTable  
    14. GO
    复制代码
    2、带有OUTPUT的DELETE的应用.

    如果要删除数据的同时,还需要记录日志,或者归档数据,在DELETE中使用OUTPUT子句在适合不过了。
    1. USE AdventureWorks;  
    2. GO  

    3. CREATE TABLE TestTable (ID INT, TEXTVal VARCHAR(100))  DECLARE @TmpTable TABLE (ID INT, TEXTVal VARCHAR(100))  

    4. INSERT TestTable (ID, TEXTVal)  VALUES (1,'FirstVal')  
    5. INSERT TestTable (ID, TEXTVal)  VALUES (2,'SecondVal')  

    6. DELETE  FROM TestTable  
    7.    OUTPUT Deleted.ID, Deleted.TEXTVal INTO @TmpTable  
    8. WHERE ID IN (1,2)  

    9. SELECT * FROM @TmpTable  
    10. SELECT * FROM TestTable  

    11. DROP TABLE TestTable  
    12. GO
    复制代码
    3、带有OUTPUT的UPDATE的应用
    1. USE AdventureWorks;  
    2. GO  

    3. CREATE TABLE TestTable (ID INT, TEXTVal VARCHAR(100))  

    4. DECLARE @TmpTable TABLE (ID_New INT, TEXTVal_New VARCHAR(100),ID_Old INT, TEXTVal_Old VARCHAR(100))  

    5. INSERT TestTable (ID, TEXTVal)  VALUES (1,'FirstVal')  
    6. INSERT TestTable (ID, TEXTVal)  VALUES (2,'SecondVal')  

    7. UPDATE TestTable   SET TEXTVal = 'NewValue'  
    8.   OUTPUT Inserted.ID, Inserted.TEXTVal, Deleted.ID, Deleted.TEXTVal INTO @TmpTable  
    9. WHERE ID IN (1,2)  

    10. SELECT * FROM @TmpTable  
    11. SELECT * FROM TestTable  

    12. DROP TABLE TestTable  
    13. GO
    复制代码
    4、在 UPDATE 语句中使用包含 from_table_name 的 OUTPUT INTO

    以下示例使用指定的
    1. ProductID
    复制代码
    1. ScrapReasonID
    复制代码
    ,针对
    1. WorkOrder
    复制代码
    表中的所有工作顺序更新
    1. ScrapReasonID
    复制代码
    列。
    1. OUTPUT INTO
    复制代码
    子句返回所更新表 (
    1. WorkOrder
    复制代码
    ) 中的值以及
    1. Product
    复制代码
    表中的值。 在
    1. Product
    复制代码
    子句中使用
    1. FROM
    复制代码
    表来指定要更新的行。
    由于
    1. WorkOrder
    复制代码
    表上定义了
    1. AFTER UPDATE
    复制代码
    触发器,因此需要
    1. INTO
    复制代码
    关键字。
    1. USE AdventureWorks2012;
    2. GO

    3. DECLARE @MyTestVar TABLE (  
    4.     OldScrapReasonID INT NOT NULL,   
    5.     NewScrapReasonID INT NOT NULL,   
    6.     WorkOrderID INT NOT NULL,  
    7.     ProductID INT NOT NULL,  
    8.     ProductName NVARCHAR(50)NOT NULL);  
    9.   
    10. UPDATE Production.WorkOrder  
    11. SET ScrapReasonID = 4  
    12. OUTPUT deleted.ScrapReasonID,  
    13.        inserted.ScrapReasonID,   
    14.        inserted.WorkOrderID,  
    15.        inserted.ProductID,  
    16.        p.Name  
    17.     INTO @MyTestVar  
    18. FROM Production.WorkOrder AS wo  
    19.     INNER JOIN Production.Product AS p   
    20.     ON wo.ProductID = p.ProductID   
    21.     AND wo.ScrapReasonID= 16  
    22.     AND p.ProductID = 733;  
    23.   
    24. SELECT OldScrapReasonID, NewScrapReasonID, WorkOrderID,   
    25.     ProductID, ProductName   
    26. FROM @MyTestVar;  
    27. GO
    复制代码
    4、MERGE语句

    下面的示例捕获从
    1. OUTPUT
    复制代码
    语句的
    1. MERGE
    复制代码
    子句返回的数据,并将这些数据插入另一个表。
    1. MERGE
    复制代码
    语句每天根据在
    1. Quantity
    复制代码
    表中处理的订单更新
    1. ProductInventory
    复制代码
    表的
    1. SalesOrderDetail
    复制代码
    列。 如果产品的库存降至
    1. 0
    复制代码
    或更低,它还会删除与这些产品对应的行。
    本示例捕获已删除的行并将这些行插入另一个表
    1. ZeroInventory
    复制代码
    中,该表跟踪没有库存的产品。
    1. USE AdventureWorks2012;  
    2. GO  
    3. IF OBJECT_ID(N'Production.ZeroInventory', N'U') IS NOT NULL  
    4.     DROP TABLE Production.ZeroInventory;  
    5. GO  
    6. --Create ZeroInventory table.  
    7. CREATE TABLE Production.ZeroInventory (DeletedProductID int, RemovedOnDate DateTime);  
    8. GO  
    9.   
    10. INSERT INTO Production.ZeroInventory (DeletedProductID, RemovedOnDate)  
    11. SELECT ProductID, GETDATE()  
    12. FROM  
    13. (   MERGE Production.ProductInventory AS pi  
    14.     USING (SELECT ProductID, SUM(OrderQty) FROM Sales.SalesOrderDetail AS sod  
    15.            JOIN Sales.SalesOrderHeader AS soh  
    16.            ON sod.SalesOrderID = soh.SalesOrderID  
    17.            AND soh.OrderDate = '20070401'  
    18.            GROUP BY ProductID) AS src (ProductID, OrderQty)  
    19.     ON (pi.ProductID = src.ProductID)  
    20.     WHEN MATCHED AND pi.Quantity - src.OrderQty <= 0  
    21.         THEN DELETE  
    22.     WHEN MATCHED  
    23.         THEN UPDATE SET pi.Quantity = pi.Quantity - src.OrderQty  
    24.     OUTPUT $action, deleted.ProductID)
    25. AS Changes (Action, ProductID)  
    26. WHERE Action = 'DELETE';  

    27. IF @@ROWCOUNT = 0  
    28. PRINT 'Warning: No rows were inserted';  
    29. GO  

    30. SELECT DeletedProductID, RemovedOnDate FROM Production.ZeroInventory;
    复制代码
    二、使用OUTPUT子句的注意事项:

    以下语句中不支持 OUTPUT 子句:

    • 引用本地分区视图、分布式分区视图或远程表的 DML 语句。
    • 包含 EXECUTE 语句的 INSERT 语句。
    • 不能将 OUTPUT INTO 子句插入视图或行集函数。
    • 参数或变量作为 UPDATE 语句的一部分进行了修改,则 OUTPUT 子句将始终返回语句执行之前的参数或变量的值而不是已修改的值
    三、C#中使用cmd.ExecuteScalar(单列)、cmdExecuteReader(多行或多列)

    返回单列:
    1. using(SqlCommand cmd=new SqlCommand("INSERT INTO Mem_Basic(Mem_Na,Mem_Occ) output INSERTED.ID VALUES(@na,@occ)",con))
    2.     {
    3.         cmd.Parameters.AddWithValue("@na", Mem_NA);
    4.         cmd.Parameters.AddWithValue("@occ", Mem_Occ);
    5.         con.Open();
    6.         int modified =(int)cmd.ExecuteScalar();
    7.         if (con.State == System.Data.ConnectionState.Open)
    8.             con.Close();
    9.        return modified;
    10.     }
    复制代码
    返回多行或者多列:
    1. create table Suspension (pkey int not null identity(1, 1),
    2. pallet_position int,
    3. processing_pallet_pkey int,
    4. datetime_created datetime,
    5. datetime_updated datetime,
    6. [this.created_by] int,
    7. [this.updated_by] int);
    8. using (var conn = new SqlConnection(connectionString))
    9. {
    10.     conn.Open();
    11.     const string insertQuery = @"
    12. INSERT INTO dbo.Suspension
    13. (pallet_position, processing_pallet_pkey, datetime_created, datetime_updated,
    14. [this.created_by], [this.updated_by])
    15. OUTPUT INSERTED.pkey VALUES
    16. (1, 2, '20141013 16:27:25.000', '20141013 16:27:25.000', 2, 2),
    17. (2, 2, '20141013 16:27:25.000', '20141013 16:27:25.000', 2, 2),
    18. (3, 2, '20141013 16:27:25.000', '20141013 16:27:25.000', 2, 2),
    19. (4, 2, '20141013 16:27:25.000', '20141013 16:27:25.000', 2, 2);";

    20.     // 通过数据库
    21.     DataTable dt = new DataTable();
    22.     using (SqlCommand cmd = new SqlCommand(insertQuery, conn))
    23.     using (var insertedOutput = cmd.ExecuteReader())
    24.     {
    25.         dt.Load(insertedOutput);
    26.     }
    27.     Console.WriteLine(dt.Rows.Count); // 4

    28.     // 通过手工读取
    29.     var list = new List<int>();
    30.     using (SqlCommand cmd = new SqlCommand(insertQuery, conn))
    31.     using (var insertedOutput = cmd.ExecuteReader())
    32.     {
    33.         while(insertedOutput.Read())
    34.         {
    35.             list.Add(insertedOutput.GetInt32(0));
    36.         }
    37.     }
    38.     Console.WriteLine(list.Count); // 4

    39.     // 通过dapper
    40.     var ids = conn.Query<int>(insertQuery).ToList();
    41.     Console.WriteLine(ids.Count); // 4
    42. }
    复制代码
    四、参考:

    https://docs.microsoft.com/zh-cn/sql/t-sql/queries/output-clause-transact-sql?view=sql-server-ver15&viewFallbackFrom=sql-server-2014
    以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

    来源:https://www.jb51.net/article/250181.htm
    免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

    最新评论

    QQ Archiver 手机版 小黑屋 福建二哥 ( 闽ICP备2022004717号|闽公网安备35052402000345号 )

    Powered by Discuz! X3.5 © 2001-2023

    快速回复 返回顶部 返回列表