OUTPUT 子句 (Transact-SQL)
更新日期: 2006 年 12 月 12 日
返回受 INSERT、UPDATE 或 DELETE 语句影响的每行的信息,或者返回基于上述每行的表达式。 这些结果可以返回到处理应用程序,以供在确认消息、存档以及其他类似的应用程序要求中使用。 此外,也可以将结果插入表或表变量。
用于:
语法
<OUTPUT_CLAUSE> ::=
{
[ OUTPUT <dml_select_list> INTO { @table_variable | output_table } [ ( column_list ) ] ]
[ OUTPUT <dml_select_list> ]
}
<dml_select_list> ::=
{ <column_name> | scalar_expression } [ [AS] column_alias_identifier ]
[ ,...n ]
<column_name> ::=
{ DELETED | INSERTED | from_table_name } . { * | column_name }
参数
@table_variable
指定一个 table 变量,返回的行将插入该变量中而不是返回到调用方。@table_variable 必须在 INSERT、UPDATE 或 DELETE 语句之前声明。如果未指定 column_list,则 table 变量必须与 OUTPUT 结果集具有相同的列数。 标识列和计算列除外,这两种列必须跳过。 如果指定了 column_list,则任何省略的列都必须允许空值,或者都分配有默认值。
有关 table 变量的详细信息,请参阅 表 (Transact-SQL)。
output_table
指定一个表,返回的行将被插入该表中而不是返回到调用方。output_table 可以为临时表。如果未指定 column_list,则表必须与 OUTPUT 结果集具有相同的列数。 标识列和计算列例外, 必须跳过这两种列。 如果指定了 column_list,则任何省略的列都必须允许空值,或者都分配有默认值。
output_table 无法应用于以下情况:
- 具有启用的对其定义的触发器。
- 参与到外键约束双方的任意一方。
- 具有 CHECK 约束或启用的规则。
- column_list
INTO 子句目标表上列名的可选列表。 它类似于 INSERT 语句中允许使用的列列表。
scalar_expression
可取计算结果为单个值的任何符号和运算符的组合。 scalar_expression 中不允许使用聚合函数。对修改的表中的列的任何引用都必须使用 INSERTED 或 DELETED 前缀限定。
- column_alias_identifier
用于引用列名的代替名称。
DELETED
指定由更新或删除操作删除的值的列前缀。 以 DELETED 为前缀的列反映 UPDATE 或 DELETE 语句完成之前的值。不能在 INSERT 语句中同时使用 DELETED 与 OUTPUT 子句。
INSERTED
列的前缀,指定由插入操作或更新操作添加的值。 以 INSERTED 为前缀的列反映 UPDATE 或 INSERT 语句完成之后但在触发器执行之前的值。INSERTED 语句不能与 DELETE 语句的 OUTPUT 子句同时使用。
from_table_name
列的前缀,指定 DELETE 语句或 UPDATE 语句(用于指定要更新或删除的行)的 FROM 子句中所包含的表。如果还在 FROM 子句中指定了要修改的表,则对该表中的列的任何引用都必须使用 INSERTED 或 DELETED 前缀限定。
*
指定受删除、插入或更新操作影响的所有列都将按照它们在表中的顺序返回。例如,以下 DELETE 语句中的
OUTPUT DELETED.*
将返回ShoppingCartItem
表中所有已删除的列:DELETE Sales.ShoppingCartItem OUTPUT DELETED.*;
- column_name
显式列引用。 任何对修改的表的引用都必须使用相应的 INSERTED 或 DELETED 前缀正确限定,例如:INSERTED**.**column_name。
备注
OUTPUT <dml_select_list> 子句和 OUTPUT <dml_select_list> INTO { @table_variable | output_table } 子句可以在单个 INSERT、UPDATE 或 DELETE 语句中定义。
注意: |
---|
除非另行指定,否则,对 OUTPUT 子句的引用将同时引用 OUTPUT 子句和 OUTPUT INTO 子句。 |
OUTPUT 子句对于在 INSERT 或 UPDATE 操作之后检索标识列或计算列的值可能非常有用。
当 <dml_select_list> 中包含计算列时,输出表或表变量中的相应列并不是计算列。 新列中的值是在执行该语句时计算出的值。
以下语句中不支持 OUTPUT 子句:
- 引用本地分区视图、分布式分区视图或远程表的 DML 语句。
- 包含 EXECUTE 语句的 INSERT 语句。
不能将 OUTPUT INTO 子句插入视图或行集函数。
无法保证将更改应用于表的顺序与将行插入输出表或表变量的顺序相对应。
如果将参数或变量作为 UPDATE 语句的一部分进行了修改,则 OUTPUT 子句将始终返回语句执行之前的参数或变量的值而不是已修改的值。
在使用 WHERE CURRENT OF 语法通过游标定位的 UPDATE 或 DELETE 语句中,可以使用 OUTPUT。
若要防止出现非确定性行为,OUTPUT 子句不能包含执行用户或系统数据访问或者假定执行此类访问的子查询或用户定义函数。 如果用户定义函数不是绑定到架构的,则假定其执行数据访问。
触发器
从 OUTPUT 中返回的列反映 INSERT、UPDATE 或 DELETE 语句完成之后但在触发器执行之前的数据。
对于 INSTEAD OF 触发器,即使没有因为触发器的操作而发生修改,也会如同实际执行 INSERT、UPDATE 或 DELETE 那样生成返回的结果。 如果在触发器的主体内使用包含 OUTPUT 子句的语句,则必须使用表别名来引用触发器 inserted 和 deleted 表,以免使用与 OUTPUT 关联的 INSERTED 和 DELETED 表复制列引用。
如果指定了 OUTPUT 子句但未同时指定 INTO 关键字,则对于给定的 DML 操作,DML 操作的目标不能启用对其定义的任何触发器。 例如,如果在 UPDATE 语句中定义了 OUTPUT 子句,则目标表不能具有任何启用的 UPDATE 触发器。
如果设置了 sp_configure 选项 disallow results from triggers,则从触发器内调用语句时,不带 INTO 子句的 OUTPUT 子句将导致该语句失败。
数据类型
OUTPUT 子句支持下列大型对象数据类型:nvarchar(max)、varchar(max)、varbinary(max)、 text、ntext、image 和 xml。 当在 UPDATE 语句中使用 .WRITE 子句修改 nvarchar(max)、varchar(max) 或 varbinary(max) 列时,如果引用了值的全部前像和后像,则将其返回。 在 OUTPUT 子句中,TEXTPTR( ) 函数不能作为 text、ntext 或 image 列的表达式的一部分出现。
队列
可以在将表用作队列或将表用于保持中间结果集的应用程序中使用 OUTPUT。 换句话说,应用程序不断地在表中添加或删除行。 以下示例在 DELETE 语句中使用 OUTPUT 子句将已删除的行返回到执行调用的应用程序。
USE AdventureWorks;
GO
DELETE TOP(1) dbo.DatabaseLog WITH (READPAST)
OUTPUT deleted.*
WHERE DatabaseLogID = 7;
GO
此示例从用作队列的表中删除一行,并使用单个操作将已删除的值返回到处理应用程序。 还可实现其他语义,例如使用表来实现堆栈。 但是,SQL Server 并不保证由使用 OUTPUT 子句的 DML 语句处理和返回行的顺序。 应用程序负责包括可保证所需语义的适当 WHERE 子句,或者理解当针对 DML 操作可能限定多行时,没有保证的顺序。 以下示例使用子查询,并假定 DatabaseLogID
列具有唯一性特征才能实现所需的排序语义。
USE tempdb
go
CREATE TABLE table1
(
id INT,
employee VARCHAR(32)
)
go
INSERT INTO table1 VALUES(1, 'Fred')
INSERT INTO table1 VALUES(2, 'Tom')
INSERT INTO table1 VALUES(3, 'Sally')
INSERT INTO table1 VALUES(4, 'Alice')
GO
DECLARE @MyTableVar TABLE
(
id INT,
employee VARCHAR(32)
)
PRINT 'table1, before delete'
SELECT * FROM table1
DELETE FROM table1
OUTPUT DELETED.* INTO @MyTableVar
WHERE id = 4 OR id = 2
PRINT 'table1, after delete'
SELECT * FROM table1
PRINT '@MyTableVar, after delete'
SELECT * FROM @MyTableVar
DROP TABLE table1
--Results
--table1, before delete
--id employee
------------- ------------------------------
--1 Fred
--2 Tom
--3 Sally
--4 Alice
--
--table1, after delete
--id employee
------------- ------------------------------
--1 Fred
--3 Sally
--@MyTableVar, after delete
--id employee
------------- ------------------------------
--2 Tom
--4 Alice
注意: |
---|
如果您的方案允许多个应用程序从一个表中执行析构性读取,请在 UPDATE 和 DELETE 语句中使用 READPAST 表提示。 这可防止在其他应用程序已经读取表中第一个限定记录的情况下出现锁定问题。 |
权限
要求对通过 <dml_select_list> 检索的任何列或者在 <scalar_expression> 中使用的任何列具有 SELECT 权限。
要求对 <output_table> 中指定的任何表具有 INSERT 权限。
示例
A. 将 OUTPUT INTO 用于简单 INSERT 语句
以下示例将行插入 ScrapReason
表,并使用 OUTPUT
子句将语句的结果返回到 @MyTableVar
table 变量。 由于 ScrapReasonID
列使用 IDENTITY 属性定义,因此未在 INSERT
语句中为该列指定一个值。 但请注意,将在列 INSERTED.ScrapReasonID
内的 OUTPUT
子句中返回由数据库引擎为该列生成的值。
USE AdventureWorks;
GO
DECLARE @MyTableVar table( ScrapReasonID smallint,
Name varchar(50),
ModifiedDate datetime);
INSERT Production.ScrapReason
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
INTO @MyTableVar
VALUES (N'Operator error', GETDATE());
--Display the result set of the table variable.
SELECT ScrapReasonID, Name, ModifiedDate FROM @MyTableVar;
--Display the result set of the table.
SELECT ScrapReasonID, Name, ModifiedDate
FROM Production.ScrapReason;
GO
B. 将 OUTPUT 用于 DELETE 语句
以下示例将删除 ShoppingCartItem
表中的所有行。 子句 OUTPUT DELETED.*
指定 DELETE
语句的结果(即已删除的行中的所有列)返回到执行调用的应用程序。 后面的 SELECT
语句验证对 ShoppingCartItem
表所执行的删除操作的结果。
USE AdventureWorks;
GO
DELETE Sales.ShoppingCartItem
OUTPUT DELETED.* ;
--Verify all rows in the table have been deleted.
SELECT COUNT(*) AS [Rows in Table] FROM Sales.ShoppingCartItem;
GO
C. 将 OUTPUT INTO 用于 UPDATE 语句
下面的示例将 Employee
表中 VacationHours
列的前 10 行更新 25%。 OUTPUT
子句将返回 VacationHours
值,该值在将列 DELETED.VacationHours
中的 UPDATE
语句和列 INSERTED.VacationHours
中的已更新值应用于 @MyTableVar
table 变量之前存在。
在它后面的两个 SELECT
语句返回 @MyTableVar
中的值以及 Employee
表中更新操作的结果。 请注意,INSERTED.ModifiedDate
列中的结果与 Employee
表中的 ModifiedDate
列不具有相同的值。 这是因为对 Employee
表定义了将 ModifiedDate
的值更新为当前日期的 AFTER UPDATE 触发器。 不过,从 OUTPUT 返回的列将反映触发器激发之前的数据。
USE AdventureWorks;
GO
DECLARE @MyTableVar table(
EmpID int NOT NULL,
OldVacationHours int,
NewVacationHours int,
ModifiedDate datetime);
UPDATE TOP (10) HumanResources.Employee
SET VacationHours = VacationHours * 1.25
OUTPUT INSERTED.EmployeeID,
DELETED.VacationHours,
INSERTED.VacationHours,
INSERTED.ModifiedDate
INTO @MyTableVar;
--Display the result set of the table variable.
SELECT EmpID, OldVacationHours, NewVacationHours, ModifiedDate
FROM @MyTableVar;
GO
--Display the result set of the table.
--Note that ModifiedDate reflects the value generated by an
--AFTER UPDATE trigger.
SELECT TOP (10) EmployeeID, VacationHours, ModifiedDate
FROM HumanResources.Employee;
GO
D. 使用 OUTPUT INTO 返回表达式
以下示例在示例 C 的基础上生成,方法是通过将 OUTPUT 子句中的表达式定义为已更新的 VacationHours
值与应用更新之前的 VacationHours
值之间的差。 该表达式的值返回到列 VacationHoursDifference
中的 @MyTableVar
table 变量。
USE AdventureWorks;
GO
DECLARE @MyTableVar table(
EmpID int NOT NULL,
OldVacationHours int,
NewVacationHours int,
VacationHoursDifference int,
ModifiedDate datetime);
UPDATE TOP (10) HumanResources.Employee
SET VacationHours = VacationHours * 1.25
OUTPUT INSERTED.EmployeeID,
DELETED.VacationHours,
INSERTED.VacationHours,
INSERTED.VacationHours - DELETED.VacationHours,
INSERTED.ModifiedDate
INTO @MyTableVar;
--Display the result set of the table variable.
SELECT EmpID, OldVacationHours, NewVacationHours,
VacationHoursDifference, ModifiedDate
FROM @MyTableVar;
GO
SELECT TOP (10) EmployeeID, VacationHours, ModifiedDate
FROM HumanResources.Employee;
GO
E. 在 UPDATE 语句中使用包含 from_table_name 的 OUTPUT INTO
以下示例使用指定的 ProductID
和 ScrapReasonID
,针对 WorkOrder
表中的所有工作顺序更新 ScrapReasonID
列。 OUTPUT INTO
子句返回所更新表 (WorkOrder
) 中的值以及 Product
表中的值。 在 FROM 子句中使用 Product
表来指定要更新的行。 由于 WorkOrder
表具有对其定义的 AFTER UPDATE 触发器,因此需要 INTO 关键字。
USE AdventureWorks;
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
F. 在 DELETE 语句中使用包含 from_table_name 的 OUTPUT INTO
以下示例将按照在 DELETE
语句的 FROM
子句中所定义的搜索条件删除 ProductProductPhoto
表中的行。 OUTPUT
子句返回所删除表(DELETED.ProductID
、DELETED.ProductPhotoID
)中的列以及 Product
表中的列。 在 FROM
子句中使用该表来指定要删除的行。
USE AdventureWorks
GO
DECLARE @MyTableVar table (
ProductID int NOT NULL,
ProductName nvarchar(50)NOT NULL,
ProductModelID int NOT NULL,
PhotoID int NOT NULL);
DELETE Production.ProductProductPhoto
OUTPUT DELETED.ProductID,
p.Name,
p.ProductModelID,
DELETED.ProductPhotoID
INTO @MyTableVar
FROM Production.ProductProductPhoto AS ph
JOIN Production.Product as p
ON ph.ProductID = p.ProductID
WHERE p.ProductModelID BETWEEN 120 and 130;
--Display the results of the table variable.
SELECT ProductID, ProductName, ProductModelID, PhotoID
FROM @MyTableVar
ORDER BY ProductModelID;
GO
G. 将 OUTPUT INTO 用于大型对象数据类型
以下示例使用 .WRITE
子句更新 Production.Document
表内 DocumentSummary
这一 nvarchar(max) 列中的部分值。 通过指定替换单词、现有数据中要替换的单词的开始位置(偏移量)以及要替换的字符数(长度),将单词 components
替换为单词 features
。 此示例使用 OUTPUT
子句将 DocumentSummary
列的前像和后像返回到 @MyTableVar
table 变量。 请注意,将返回 DocumentSummary
列的全部前像和后像。
USE AdventureWorks;
GO
DECLARE @MyTableVar table (
DocumentID int NOT NULL,
SummaryBefore nvarchar(max),
SummaryAfter nvarchar(max));
UPDATE Production.Document
SET DocumentSummary .WRITE (N'features',28,10)
OUTPUT INSERTED.DocumentID,
DELETED.DocumentSummary,
INSERTED.DocumentSummary
INTO @MyTableVar
WHERE DocumentID = 3 ;
SELECT DocumentID, SummaryBefore, SummaryAfter
FROM @MyTableVar;
GO
H. 在 INSTEAD OF 触发器中使用 OUTPUT
以下示例在触发器中使用 OUTPUT 子句返回触发器操作的结果。 首先,创建一个 ScrapReason
表的视图,然后对该视图定义 INSTEAD OF INSERT
触发器,从而使用户只修改基表的 Name
列。 由于列 ScrapReasonID
是基表中的 IDENTITY 列,因此触发器将忽略用户提供的值。 这允许数据库引擎自动生成正确的值。 同样,用户为 ModifiedDate
提供的值也被忽略并设置为正确的日期。 OUTPUT
子句返回实际插入 ScrapReason
表中的值。
USE AdventureWorks;
GO
IF OBJECT_ID('dbo.vw_ScrapReason','V') IS NOT NULL
DROP VIEW dbo.vw_ScrapReason;
GO
CREATE VIEW dbo.vw_ScrapReason
AS (SELECT ScrapReasonID, Name, ModifiedDate
FROM Production.ScrapReason);
GO
CREATE TRIGGER dbo.io_ScrapReason
ON dbo.vw_ScrapReason
INSTEAD OF INSERT
AS
BEGIN
--ScrapReasonID is not specified in the list of columns to be inserted
--because it is an IDENTITY column.
INSERT INTO Production.ScrapReason (Name, ModifiedDate)
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name,
INSERTED.ModifiedDate
SELECT Name, getdate()
FROM inserted;
END
GO
INSERT vw_ScrapReason (ScrapReasonID, Name, ModifiedDate)
VALUES (99, N'My scrap reason','20030404');
GO
这是在 2004 年 4 月 12 日 ('2004-04-12'
) 生成的结果集。 请注意,ScrapReasonIDActual
和 ModifiedDate
列反映由触发器操作生成的值而不是 INSERT
语句中提供的值。
ScrapReasonID Name ModifiedDate
------------- ---------------- -----------------------
17 My scrap reason 2004-04-12 16:23:33.050
I. 将 OUTPUT INTO 用于标识列和计算列
下面的示例创建 EmployeeSales
表,然后使用 INSERT 语句向其中插入若干行,并使用 SELECT 语句从源表中检索数据。 EmployeeSales
表包含标识列 (EmployeeID
) 和计算列 (ProjectedSales
)。 由于这些值是在插入操作期间由 SQL Server 数据库引擎生成的,因此,不能在 @MyTableVar
中定义上述两列。
USE AdventureWorks ;
GO
IF OBJECT_ID ('dbo.EmployeeSales', 'U') IS NOT NULL
DROP TABLE dbo.EmployeeSales;
GO
CREATE TABLE dbo.EmployeeSales
( EmployeeID int IDENTITY (1,5)NOT NULL,
LastName nvarchar(20) NOT NULL,
FirstName nvarchar(20) NOT NULL,
CurrentSales money NOT NULL,
ProjectedSales AS CurrentSales * 1.10
);
GO
DECLARE @MyTableVar table(
LastName nvarchar(20) NOT NULL,
FirstName nvarchar(20) NOT NULL,
CurrentSales money NOT NULL
);
INSERT INTO dbo.EmployeeSales (LastName, FirstName, CurrentSales)
OUTPUT INSERTED.LastName,
INSERTED.FirstName,
INSERTED.CurrentSales
INTO @MyTableVar
SELECT c.LastName, c.FirstName, sp.SalesYTD
FROM HumanResources.Employee AS e
INNER JOIN Sales.SalesPerson AS sp
ON e.EmployeeID = sp.SalesPersonID
INNER JOIN Person.Contact AS c
ON e.ContactID = c.ContactID
WHERE e.EmployeeID LIKE '2%'
ORDER BY c.LastName, c.FirstName;
SELECT LastName, FirstName, CurrentSales
FROM @MyTableVar;
GO
SELECT EmployeeID, LastName, FirstName, CurrentSales, ProjectedSales
FROM dbo.EmployeeSales;
GO
J. 在单个语句中使用 OUTPUT 和 OUTPUT INTO
以下示例将按照在 DELETE
语句的 FROM
子句中所定义的搜索条件删除 ProductProductPhoto
表中的行。 OUTPUT INTO
子句将所删除表(DELETED.ProductID
、DELETED.ProductPhotoID
)中的列以及 Product
表中的列返回到 @MyTableVar
table 变量。 在 FROM
子句中使用 Product
表来指定要删除的行。 OUTPUT
子句将 ProductProductPhoto
表中的 DELETED.ProductID
、DELETED.ProductPhotoID
列以及行的删除日期和时间返回到执行调用的应用程序。
USE AdventureWorks
GO
DECLARE @MyTableVar table (
ProductID int NOT NULL,
ProductName nvarchar(50)NOT NULL,
ProductModelID int NOT NULL,
PhotoID int NOT NULL);
DELETE Production.ProductProductPhoto
OUTPUT DELETED.ProductID,
p.Name,
p.ProductModelID,
DELETED.ProductPhotoID
INTO @MyTableVar
OUTPUT DELETED.ProductID, DELETED.ProductPhotoID, GETDATE() AS DeletedDate
FROM Production.ProductProductPhoto AS ph
JOIN Production.Product as p
ON ph.ProductID = p.ProductID
WHERE p.ProductID BETWEEN 800 and 810;
--Display the results of the table variable.
SELECT ProductID, ProductName, PhotoID, ProductModelID
FROM @MyTableVar;
GO
请参阅
参考
DELETE (Transact-SQL)
INSERT (Transact-SQL)
UPDATE (Transact-SQL)
表 (Transact-SQL)
CREATE TRIGGER (Transact-SQL)
sp_configure (Transact-SQL)
帮助和信息
更改历史记录
发布日期 | 历史记录 |
---|---|
2006 年 12 月 12 日 |
|