expression 是复制到 column_name 的值 。 expression 的计算结果必须为 column_name 类型或者 expression 必须能够隐式强制转换为此类型 。 如果 expression 设置为 NULL,则忽略 @Length,并将 column_name 中的值按指定的 @Offset 截断 。
@Offset 是存储在 column_name 中的值的起点,从该点开始编写 expression 。 @Offset 是从 0 开始的序号字节位置,数据类型为 bigint,不能为负数。 如果 @Offset 为 NULL,则更新操作将在现有 column_name 值的结尾追加 expression,并忽略 @Length 。 如果 @Offset 大于 column_name 值的字节长度,则 数据库引擎 将返回错误 。 如果 @Offset 加上 @Length 超出了列中基础值的限度,则将删除到值的最后一个字符 。
@Length 是指列中某个部分的长度,从 @Offset 开始,该长度由 expression 替换 。 @Length 是 bigint 并且不能为负数。 如果 @Length 为 NULL,则更新操作将删除从 @Offset 到 column_name 值的结尾的所有数据 。
如果所更新对象与 FROM 子句中的对象相同,并且在 FROM 子句中对该对象只有一个引用,则指定或不指定对象别名均可。 如果更新的对象在 FROM 子句中出现了不止一次,则对该对象的一个(并且只有一个)引用不能指定表别名。 FROM 子句中对该对象的所有其他引用都必须包含对象别名。
FROM 子句中对 OPENDATASOURCE、OPENQUERY 或 OPENROWSET 的任何调用与对用作更新目标的这些函数的任何调用都是分开独立计算的,即使为两个调用提供的参数相同也是如此。 具体而言,应用到上述任一调用的结果的筛选器或联接条件不会影响其他调用的结果。
定位更新使用 CURRENT OF 子句指定游标。 更新操作发生在游标的当前位置。
<search_condition>
为要更新的行指定需满足的条件。 搜索条件也可以是联接所基于的条件。 对搜索条件中可以包含的谓词数量没有限制。 有关谓词和搜索条件的详细信息,请参阅搜索条件 (Transact-SQL)。
CURRENT OF
指定更新在指定游标的当前位置进行。
使用 WHERE CURRENT OF 子句的定位更新将在游标的当前位置更新单行。 这比使用 WHERE <search_condition> 子句限定所更新行的搜索更新更精确。 当搜索条件不唯一标识一行时,搜索更新将修改多行。
GLOBAL
指定 cursor_name 是指全局游标。
cursor_name
要从中进行提取的开放游标的名称。 如果同时存在名为 cursor_name 的全局游标和局部游标,那么,在指定了 GLOBAL 时,该参数是指全局游标;否则是指局部游标。 游标必须允许更新。
cursor_variable_name
cursor 变量的名称。 cursor_variable_name 必须引用允许更新的游标。
OPTION (<query_hint> [ ,... n ] )
指定优化器提示用于自定义数据库引擎处理语句的方式。 有关详细信息,请参阅 查询提示 (Transact-SQL)。
使用 @@ROWCOUNT
函数返回插入到客户端应用程序的行数。 有关详细信息,请参阅 @@ROWCOUNT (Transact-SQL)。
可以在 UPDATE 语句中使用变量名称来显示受影响的旧值和新值,但仅当 UPDATE 语句影响单个记录时才应使用变量名称。 如果 UPDATE 语句影响多个记录,若要返回每个记录的旧值和新值,请使用 OUTPUT 子句。
指定 FROM 子句为更新操作提供条件时务须小心。 如果 UPDATE 语句包含了未指定每个更新列的位置只有一个可用值的 FROM 子句(换句话说,如果 UPDATE 语句是不确定性的),则其结果将不明确。 例如,对于下面脚本中的 UPDATE 语句,Table1
中的全部两行都满足 UPDATE 语句中 FROM 子句的限定条件;但是,将使用 Table1
中的哪一行来更新 Table2.
中的行是不明确的。
USE AdventureWorks2022;
IF OBJECT_ID ('dbo.Table1', 'U') IS NOT NULL
DROP TABLE dbo.Table1;
IF OBJECT_ID ('dbo.Table2', 'U') IS NOT NULL
DROP TABLE dbo.Table2;
CREATE TABLE dbo.Table1
(ColA INT NOT NULL, ColB DECIMAL(10,3) NOT NULL);
CREATE TABLE dbo.Table2
(ColA INT PRIMARY KEY NOT NULL, ColB DECIMAL(10,3) NOT NULL);
INSERT INTO dbo.Table1 VALUES(1, 10.0), (1, 20.0);
INSERT INTO dbo.Table2 VALUES(1, 0.0);
UPDATE dbo.Table2
SET dbo.Table2.ColB = dbo.Table2.ColB + dbo.Table1.ColB
FROM dbo.Table2
INNER JOIN dbo.Table1
ON (dbo.Table2.ColA = dbo.Table1.ColA);
SELECT ColA, ColB
FROM dbo.Table2;
当结合使用 FROM
和 WHERE CURRENT OF
子句时,可能发生同样的问题。 在以下示例中,Table2
中的全部两行都满足 FROM
语句中 UPDATE
子句的限定条件。 将使用 Table2
的哪一行来更新 Table1
中的行是不明确的。
USE AdventureWorks2022;
IF OBJECT_ID ('dbo.Table1', 'U') IS NOT NULL
DROP TABLE dbo.Table1;
IF OBJECT_ID ('dbo.Table2', 'U') IS NOT NULL
DROP TABLE dbo.Table2;
CREATE TABLE dbo.Table1
(c1 INT PRIMARY KEY NOT NULL, c2 INT NOT NULL);
CREATE TABLE dbo.Table2
(d1 INT PRIMARY KEY NOT NULL, d2 INT NOT NULL);
INSERT INTO dbo.Table1 VALUES (1, 10);
INSERT INTO dbo.Table2 VALUES (1, 20), (2, 30);
DECLARE abc CURSOR LOCAL FOR
SELECT c1, c2
FROM dbo.Table1;
OPEN abc;
FETCH abc;
UPDATE dbo.Table1
SET c2 = c2 + d2
FROM dbo.Table2
WHERE CURRENT OF abc;
SELECT c1, c2 FROM dbo.Table1;
兼容性支持
在 SQL Server 的未来版本中,将不再支持在 FROM 子句中使用应用于 UPDATE 或 DELETE 语句目标表的 READUNCOMMITTED 和 NOLOCK 提示。 请避免在新的开发工作上下文中使用这些提示,并计划修改当前使用它们的应用程序。
所有的 char 和 nchar 列向右填充至定义长度 。
如果 ANSI_PADDING 设置为 OFF,则会从插入 varchar 和 nvarchar 列的数据中删除所有尾随空格,但只包含空格的字符串除外 。 这些字符串被截断为空字符串。 如果 ANSI_PADDING 设置为 ON,则插入尾随空格。 Microsoft SQL Server ODBC 驱动程序和用于 SQL Server 的 OLE DB 访问接口自动对每个连接设置 ANSI_PADDING ON。 这可在 ODBC 数据源中进行配置,也可通过设置连接特性或属性进行配置。 有关详细信息,请参阅 SET ANSI_PADDING (Transact-SQL)。
更新 text、ntext 和 image 列
使用 UPDATE 修改 text、ntext 或 image 列时将对列进行初始化,向其列分配有效的文本指针,并且分配至少一个数据页(除非使用 NULL 更新该列) 。
若要替换或修改大型 text、ntext 或 image 数据块,请使用 WRITETEXT 或 UPDATETEXT,而不使用 UPDATE 语句 。
如果 UPDATE 语句在更新聚集键以及一个或者多个 text、ntext 或 image 列时可以更改多个行,则对这些列的部分更新将作为替换所有值来执行 。
Microsoft SQL Server 的未来版本中将删除 ntext、text 和 image 数据类型。 请避免在新开发工作中使用这些数据类型,并考虑修改当前使用这些数据类型的应用程序。 请改用 nvarchar(max)、 varchar(max)和 varbinary(max) 。
更新大值数据类型
使用 .WRITE (expression,@Offset,@Length) 子句执行 varchar(max)、nvarchar(max) 和 varbinary(max) 数据类型的部分或完整更新。
例如,对 varchar(max) 列的部分更新可能只删除或修改该列的前 200 个字节(如果使用的是 ASCII 字符,则为前 200 个字符),而完整更新则删除或修改该列中的所有数据。 如果将数据库恢复模式设置为大容量日志模式或简单模式,则对插入或追加新数据的 .WRITE 更新进行最小日志记录。 在更新现有值时,不使用最小日志记录。 有关详细信息,请参阅事务日志 (SQL Server)。
当 UPDATE 语句导致下列任一操作时,数据库引擎便会将部分更新转换为完整更新:
更改分区视图或表的键列。
修改多行并且还将非唯一的聚集索引的键更新为非常量值。
不能使用 .WRITE 子句更新 NULL 列或将 column_name 的值设置为 NULL。
对于 varbinary 和 varchar 数据类型,以字节为单位指定 @Offset 和 @Length;对于 nvarchar 数据类型,则以字节对为单位进行指定 。 有关字符串数据类型长度的详细信息,请参阅 char 和 varchar (Transact-SQL) 以及 nchar 和 nvarchar (Transact-SQL)。
为了获得最佳性能,建议按照块区大小为 8040 字节倍数的方式插入或更新数据。
如果在 OUTPUT 子句中引用了由 .WRITE 子句修改的列,则该列的完整值(deleted.column_name 中的前像或 inserted.column_name 中的后像)返回到表变量中的指定列。 请参阅后面的示例 R。
若要针对其他字符或二进制数据类型获得相同的 .WRITE 功能,请使用 STUFF (Transact-SQL)。
更新用户定义类型列
更新用户定义类型列中的值可以通过下列方式之一完成:
提供 SQL Server 系统数据类型的值,条件是该用户定义类型支持该类型的隐式转换或显式转换。 以下示例显示如何通过从字符串显式转换来更新用户定义类型 Point
的列中的值。
UPDATE Cities
SET Location = CONVERT(Point, '12.3:46.2')
WHERE Name = 'Anchorage';
调用标记为“赋值函数”的用户定义类型的方法执行更新。 以下示例调用类型 Point
的名为 SetXY
的赋值函数方法。 这将更新该类型的实例状态。
UPDATE Cities
SET Location.SetXY(23.5, 23.5)
WHERE Name = 'Anchorage';
如果对 Transact-SQL Null 值调用赋值函数方法,或者赋值函数方法产生的新值为 Null,则 SQL Server 将返回错误。
修改用户定义类型的已注册属性或公共数据成员的值。 提供值的表达式必须可隐式转换为属性的类型。 以下示例修改用户定义类型 X
的属性 Point
的值。
UPDATE Cities
SET Location.X = 23.5
WHERE Name = 'Anchorage';
若要修改同一用户定义类型列的不同属性,请发出多个 UPDATE 语句,或者调用该类型的赋值函数方法。
更新 FILESTREAM 数据
您可以使用 UPDATE 语句将 FILESTREAM 字段更新为 null 值、空值或相对较小的内联数据量。 但是,使用 Win32 接口可以更有效地将大量数据以流的方式导入到文件中。 更新 FILESTREAM 字段时,即会修改文件系统中的基础 BLOB 数据。 将 FILESTREAM 字段设置为 NULL 即会删除与该字段相关联的 BLOB 数据。 不能使用 .WRITE(), 执行对 FILESTREAM 数据的部分更新。 有关详细信息,请参阅 FILESTREAM (SQL Server)。
错误处理。
如果对行的更新违反了某个约束或规则,或违反了对列的 NULL 设置,或者新值是不兼容的数据类型,则取消该语句、返回错误并且不更新任何记录。
当 UPDATE 语句在表达式求值过程中遇到算术错误(溢出、被零除或域错误)时,则不进行更新。 批处理的剩余部分不再执行,并且返回错误消息。
如果对参与聚集索引的一列或多列的更新导致聚集索引和行的大小超过 8,060 字节,则更新失败并且返回错误消息。
仅当所修改的表是表变量时,才允许在用户定义函数的主体中使用 UPDATE 语句。
当对表的 UPDATE 操作定义 INSTEAD OF
触发器时,将运行触发器而不运行 UPDATE 语句。 早期版本的 SQL Server 只支持对 UPDATE 和其他数据修改语句定义 AFTER 触发器。 不能在直接或间接引用定义有 INSTEAD OF
触发器的视图的 UPDATE 语句中指定 FROM 子句。 有关 INSTEAD OF 触发器的详细信息,请参阅 CREATE TRIGGER (Transact-SQL)。
目前,无法在 Microsoft Fabric 中的仓库的 UPDATE 语句中指定 FROM 子句。 支持单表 UPDATE 语句。
限制和局限
不能在直接或间接引用定义有 INSTEAD OF
触发器的视图的 UPDATE 语句中指定 FROM 子句。 有关 INSTEAD OF
触发器的详细信息,请参阅 CREATE TRIGGER (Transact-SQL)。
在公用表表达式 (CTE) 是 UPDATE 语句的目标时,在该语句中对 CTE 的所有引用都必须匹配。 例如,如果在 FROM 子句中向 CTE 分配了一个别名,则该别名必须用于对 CTE 的所有其他引用。 需要明确的 CTE 引用,因为 CTE 没有对象 ID,而 SQL Server 使用对象 ID 来识别对象与其别名之间的隐式关系。 如果没有这一关系,查询计划可能会产生意外的联接行为和意外的查询结果。 以下示例演示在 CTE 是更新操作的目标对象时指定 CTE 的正确和不正确的方法。
USE tempdb;
-- UPDATE statement with CTE references that are correctly matched.
DECLARE @x TABLE (ID INT, Value INT);
DECLARE @y TABLE (ID INT, Value INT);
INSERT @x VALUES (1, 10), (2, 20);
INSERT @y VALUES (1, 100),(2, 200);
WITH cte AS (SELECT * FROM @x)
UPDATE x -- cte is referenced by the alias.
SET Value = y.Value
FROM cte AS x -- cte is assigned an alias.
INNER JOIN @y AS y ON y.ID = x.ID;
SELECT * FROM @x;
下面是结果集。
ID Value
------ -----
1 100
2 200
(2 row(s) affected)
使用不正确匹配的 CTE 引用的 UPDATE 语句。
USE tempdb;
DECLARE @x TABLE (ID INT, Value INT);
DECLARE @y TABLE (ID INT, Value INT);
INSERT @x VALUES (1, 10), (2, 20);
INSERT @y VALUES (1, 100),(2, 200);
WITH cte AS (SELECT * FROM @x)
UPDATE cte -- cte is not referenced by the alias.
SET Value = y.Value
FROM cte AS x -- cte is assigned an alias.
INNER JOIN @y AS y ON y.ID = x.ID;
SELECT * FROM @x;
下面是结果集。
ID Value
------ -----
1 100
2 100
(2 row(s) affected)
UPDATE 语句在其修改的任何行上获取排他 (X) 锁,并在事务完成之前持有这些锁。 根据 UPDATE 语句的查询计划、要修改的行数和事务的隔离级别,可以在 PAGE 级或 TABLE 级(而不是 ROW 级)获取这些锁。 为避免这些更高级别的锁,可以考虑将影响数千行或更多行的 update 语句分成若干个批次,并确保索引支持任何联接和筛选条件。 有关 SQL Server 中的锁定机制的详细信息,请参阅数据库引擎中的锁定一文。
日志记录行为
UPDATE 语句将记入日志;但是,对使用 .WRITE 子句对较大值数据类型的部分更新进行最小日志记录。 有关详细信息,请参阅上一节“数据类型”中的“更新大值数据类型”。
要求对目标表具有 UPDATE
权限。 如果 UPDATE 语句包含 WHERE 子句,或者 SET 子句中的 expression 使用了表中的某个列,则还要求对要更新的表具有 SELECT
权限。
UPDATE 权限默认授予 sysadmin
固定服务器角色、db_owner
和 db_datawriter
固定数据库角色的成员以及表所有者。 sysadmin
、db_owner
和 db_securityadmin
角色的成员以及表所有者可以将权限转让给其他用户。
作为特征的语法元素
B. 更新多个列
以下示例对于 Bonus
表中的所有行更新 CommissionPct
、SalesQuota
和 SalesPerson
列中的值。
USE AdventureWorks2022;
UPDATE Sales.SalesPerson
SET Bonus = 6000, CommissionPct = .10, SalesQuota = NULL;
限制更新的行
本节中的示例说明了可用于限制 UPDATE 语句所影响的行数的方法。
C. 使用 WHERE 子句
以下示例使用 WHERE 子句指定要更新的行。 该语句对于 Color
列中已具有值“Red”且在以“Road-250”开头的 Production.Product
列中具有值的所有行更新 Color
表中 Name
列的值。
USE AdventureWorks2022;
UPDATE Production.Product
SET Color = N'Metallic Red'
WHERE Name LIKE N'Road-250%' AND Color = N'Red';
D. 使用 TOP 子句
以下示例使用 TOP 子句来限制 UPDATE 语句中修改的行数。 当 TOP (n) 子句与 UPDATE 一起使用时,将针对随机选择的“n”行执行更新操作 。 以下示例按照 VacationHours
表中 10 个随机行的 25% 更新 Employee
列。
USE AdventureWorks2022;
UPDATE TOP (10) HumanResources.Employee
SET VacationHours = VacationHours * 1.25 ;
如果必须使用 TOP 来应用按有意义的时间顺序排列的更新,则必须在嵌套 select 语句中同时使用 TOP 和 ORDER BY。 下列示例更新了雇佣最早的 10 名雇员的假期小时数。
UPDATE HumanResources.Employee
SET VacationHours = VacationHours + 8
FROM (SELECT TOP 10 BusinessEntityID FROM HumanResources.Employee
ORDER BY HireDate ASC) AS th
WHERE HumanResources.Employee.BusinessEntityID = th.BusinessEntityID;
E. 使用 WITH common_table_expression 子句
以下示例为直接或间接用于创建 PerAssemblyQty
的所有部件和组件更新 ProductAssemblyID 800
值。 公用表表达式将返回用于直接生成 ProductAssemblyID 800
的部件和用于生成这些组件的部件等的层次结构列表。 只修改公用表表达式所返回的行。
USE AdventureWorks2022;
WITH Parts(AssemblyID, ComponentID, PerAssemblyQty, EndDate, ComponentLevel) AS
SELECT b.ProductAssemblyID, b.ComponentID, b.PerAssemblyQty,
b.EndDate, 0 AS ComponentLevel
FROM Production.BillOfMaterials AS b
WHERE b.ProductAssemblyID = 800
AND b.EndDate IS NULL
UNION ALL
SELECT bom.ProductAssemblyID, bom.ComponentID, p.PerAssemblyQty,
bom.EndDate, ComponentLevel + 1
FROM Production.BillOfMaterials AS bom
INNER JOIN Parts AS p
ON bom.ProductAssemblyID = p.ComponentID
AND bom.EndDate IS NULL
UPDATE Production.BillOfMaterials
SET PerAssemblyQty = c.PerAssemblyQty * 2
FROM Production.BillOfMaterials AS c
JOIN Parts AS d ON c.ProductAssemblyID = d.AssemblyID
WHERE d.ComponentLevel = 0;
F. 使用 WHERE CURRENT OF 子句
以下示例使用 WHERE CURRENT OF 子句来只更新游标位于其上的行。 如果游标基于某个联接,则只修改 UPDATE 语句中指定的 table_name
。 其他参与该游标的表不会受到影响。
USE AdventureWorks2022;
DECLARE complex_cursor CURSOR FOR
SELECT a.BusinessEntityID
FROM HumanResources.EmployeePayHistory AS a
WHERE RateChangeDate <>
(SELECT MAX(RateChangeDate)
FROM HumanResources.EmployeePayHistory AS b
WHERE a.BusinessEntityID = b.BusinessEntityID) ;
OPEN complex_cursor;
FETCH FROM complex_cursor;
UPDATE HumanResources.EmployeePayHistory
SET PayFrequency = 2
WHERE CURRENT OF complex_cursor;
CLOSE complex_cursor;
DEALLOCATE complex_cursor;
本节中的示例说明了如何使用计算值、子查询和 DEFAULT 值来更新列。
G. 指定计算值
以下示例在 UPDATE 语句中使用计算值。 该示例将 Product
表中所有行的 ListPrice
列的值加倍。
USE AdventureWorks2022;
UPDATE Production.Product
SET ListPrice = ListPrice * 2;
H. 指定复合运算符
下面的示例使用变量 @NewPrice
通过在当前价格基础上加 10 来提高所有红色自行车的价格。
USE AdventureWorks2022;
DECLARE @NewPrice INT = 10;
UPDATE Production.Product
SET ListPrice += @NewPrice
WHERE Color = N'Red';
以下示例使用复合运算符 += 针对 ' - tool malfunction'
为 10 到 12 的行将数据 Name
追加到列 ScrapReasonID
中的现有值之后。
USE AdventureWorks2022;
UPDATE Production.ScrapReason
SET Name += ' - tool malfunction'
WHERE ScrapReasonID BETWEEN 10 and 12;
I. 在 SET 子句中指定子查询
以下示例使用 SET 子句中的子查询来确定用于更新列的值。 子查询必须只返回标量值(即每行返回一个值)。 此示例修改 SalesYTD
表中的 SalesPerson
列,以反映 SalesOrderHeader
表中记录的最近销售情况。 该子查询在 UPDATE
语句中聚合了每个销售人员的销售量。
USE AdventureWorks2022;
UPDATE Sales.SalesPerson
SET SalesYTD = SalesYTD +
(SELECT SUM(so.SubTotal)
FROM Sales.SalesOrderHeader AS so
WHERE so.OrderDate = (SELECT MAX(OrderDate)
FROM Sales.SalesOrderHeader AS so2
WHERE so2.SalesPersonID = so.SalesPersonID)
AND Sales.SalesPerson.BusinessEntityID = so.SalesPersonID
GROUP BY so.SalesPersonID);
J. 使用 DEFAULT 值更新行
以下示例针对 CostRate
值大于 CostRate
的所有行将 20.00
列设置为其默认值 (0.00)。
USE AdventureWorks2022;
UPDATE Production.Location
SET CostRate = DEFAULT
WHERE CostRate > 20.00;
指定目标对象,而非标准表
本节中的示例说明了如何通过指定视图、表别名或表变量来更新行。
K. 将视图指定为目标对象
以下示例通过将视图指定为目标对象来更新表中的行。 该视图定义引用了多个表,但是 UPDATE 语句成功运行,因为它只引用了其中一个基础表中的列。 如果指定两个表中的列,UPDATE 语句将失败。 有关详细信息,请参阅通过视图修改数据。
USE AdventureWorks2022;
UPDATE Person.vStateProvinceCountryRegion
SET CountryRegionName = 'United States of America'
WHERE CountryRegionName = 'United States';
L. 将表别名指定为目标对象
以下示例将更新 Production.ScrapReason
表中的行。 将分配给 FROM 子句中 ScrapReason
的表别名指定为 UPDATE 子句中的目标对象。
USE AdventureWorks2022;
UPDATE sr
SET sr.Name += ' - tool malfunction'
FROM Production.ScrapReason AS sr
JOIN Production.WorkOrder AS wo
ON sr.ScrapReasonID = wo.ScrapReasonID
AND wo.ScrappedQty > 300;
M. 将表变量指定为目标对象
以下示例将更新表变量中的行。
USE AdventureWorks2022;
-- Create the table variable.
DECLARE @MyTableVar TABLE (
EmpID INT NOT NULL,
NewVacationHours INT,
ModifiedDate DATETIME);
-- Populate the table variable with employee ID values from HumanResources.Employee.
INSERT INTO @MyTableVar (EmpID)
SELECT BusinessEntityID FROM HumanResources.Employee;
-- Update columns in the table variable.
UPDATE @MyTableVar
SET NewVacationHours = e.VacationHours + 20,
ModifiedDate = GETDATE()
FROM HumanResources.Employee AS e
WHERE e.BusinessEntityID = EmpID;
-- Display the results of the UPDATE statement.
SELECT EmpID, NewVacationHours, ModifiedDate FROM @MyTableVar
ORDER BY EmpID;
基于其他表中的数据更新数据
本节中的示例说明了基于一个表中的信息更新另一个表中的行的方法。
下面的示例修改 SalesYTD
表中的 SalesPerson
列,以反映 SalesOrderHeader
表中记录的最近销售情况。
USE AdventureWorks2022;
UPDATE Sales.SalesPerson
SET SalesYTD = SalesYTD + SubTotal
FROM Sales.SalesPerson AS sp
JOIN Sales.SalesOrderHeader AS so
ON sp.BusinessEntityID = so.SalesPersonID
AND so.OrderDate = (SELECT MAX(OrderDate)
FROM Sales.SalesOrderHeader
WHERE SalesPersonID = sp.BusinessEntityID);
上一个示例假定在特定日期只记录指定销售人员的一笔销售业务,并假定更新信息是最新的。 如果在同一天中可以记录指定销售人员的多笔销售业务,所示的示例将不能正常运行。 该示例运行时没有错误,但是每个 SalesYTD
值只能用一笔销售数据更新,而不管那一天实际发生了多少笔销售业务。 这是因为一条 UPDATE 语句永远不会对同一行更新两次。
对于特定销售人员在同一天可销售不止一批的情况,每个销售人员的所有销售量必须在 UPDATE
语句中合计在一起,如下例所示:
USE AdventureWorks2022;
UPDATE Sales.SalesPerson
SET SalesYTD = SalesYTD +
(SELECT SUM(so.SubTotal)
FROM Sales.SalesOrderHeader AS so
WHERE so.OrderDate = (SELECT MAX(OrderDate)
FROM Sales.SalesOrderHeader AS so2
WHERE so2.SalesPersonID = so.SalesPersonID)
AND Sales.SalesPerson.BusinessEntityID = so.SalesPersonID
GROUP BY so.SalesPersonID);
更新远程表中的行
本节中的示例说明了如何使用链接服务器或行集函数引用一个远程目标表,从而更新该表中的行。
O. 使用链接服务器更新远程表中的数据
以下示例更新远程服务器上的表。 该示例从使用 sp_addlinkedserver 创建指向远程数据源的链接开始。 然后,将链接服务器名称 MyLinkedServer
指定为 server.catalog.schema.object 形式的由四个部分组成的对象名称的一部分。 请注意,您必须为 @datasrc
指定有效的服务器名称。
USE master;
-- Create a link to the remote data source.
-- Specify a valid server name for @datasrc as 'server_name' or 'server_nameinstance_name'.
EXEC sp_addlinkedserver @server = N'MyLinkedServer',
@srvproduct = N' ',
@provider = N'SQLNCLI10',
@datasrc = N'<server name>',
@catalog = N'AdventureWorks2022';
USE AdventureWorks2022;
-- Specify the remote data source using a four-part name
-- in the form linked_server.catalog.schema.object.
UPDATE MyLinkedServer.AdventureWorks2022.HumanResources.Department
SET GroupName = N'Public Relations'
WHERE DepartmentID = 4;
P. 使用 OPENQUERY 函数更新远程表中的数据
以下示例通过指定 OPENQUERY 行集函数来更新远程表中的行。 在之前例子中创建的链接服务器名称用于此示例。
UPDATE OPENQUERY (MyLinkedServer, 'SELECT GroupName FROM HumanResources.Department WHERE DepartmentID = 4')
SET GroupName = 'Sales and Marketing';
Q. 使用 OPENDATASOURCE 函数更新远程表中的数据
以下示例通过指定 OPENDATASOURCE 行集函数来更新远程表中的行。 通过使用 server_name 或 server_name\instance_name 格式,为该数据源指定一个有效的服务器名称 。 您可能需要为即席分布式查询配置 SQL Server 实例。 有关详细信息,请参阅即席分布式查询服务器配置选项。
UPDATE OPENDATASOURCE('SQLNCLI', 'Data Source=<server name>;Integrated Security=SSPI').AdventureWorks2022.HumanResources.Department
SET GroupName = 'Sales and Marketing' WHERE DepartmentID = 4;
更新大型对象数据类型
本节中的示例说明了如何更新使用大型对象 (LOB) 数据类型定义的列中的值。
R. 使用包含 .WRITE 的 UPDATE 来修改 nvarchar(max) 列中的数据
以下示例使用 .WRITE 子句更新 DocumentSummary
(Production.Document
表内的 nvarchar(max) 列)中的部分值。 通过指定替换单词、现有数据中要替换的单词的开始位置(偏移量)以及要替换的字符数(长度),将单词 components
替换为单词 features
。 此示例还使用 OUTPUT 子句将 DocumentSummary
列的前像和后像返回给 @MyTableVar
表变量。
USE AdventureWorks2022;
DECLARE @MyTableVar TABLE (
SummaryBefore NVARCHAR(max),
SummaryAfter NVARCHAR(max));
UPDATE Production.Document
SET DocumentSummary .WRITE (N'features',28,10)
OUTPUT deleted.DocumentSummary,
inserted.DocumentSummary
INTO @MyTableVar
WHERE Title = N'Front Reflector Bracket Installation';
SELECT SummaryBefore, SummaryAfter
FROM @MyTableVar;
S. 使用包含 .WRITE 的 UPDATE 在 nvarchar(max) 列中添加和删除数据
以下示例从当前值设置为 NULL 的 nvarchar(max) 列中添加和删除列。 由于不能使用 .WRITE 子句修改 NULL 列,因此先使用临时数据填充该列。 然后,使用 .WRITE 子句将该数据替换为正确的数据。 其他示例将数据追加到列值的结尾,从列中删除(截断)数据,最后从列中删除部分数据。 SELECT 语句显示由每个 UPDATE 语句生成的数据修改。
USE AdventureWorks2022;
-- Replacing NULL value with temporary data.
UPDATE Production.Document
SET DocumentSummary = N'Replacing NULL value'
WHERE Title = N'Crank Arm and Tire Maintenance';
SELECT DocumentSummary
FROM Production.Document
WHERE Title = N'Crank Arm and Tire Maintenance';
-- Replacing temporary data with the correct data. Setting @Length to NULL
-- truncates all existing data from the @Offset position.
UPDATE Production.Document
SET DocumentSummary .WRITE(N'Carefully inspect and maintain the tires and crank arms.',0,NULL)
WHERE Title = N'Crank Arm and Tire Maintenance';
SELECT DocumentSummary
FROM Production.Document
WHERE Title = N'Crank Arm and Tire Maintenance';
-- Appending additional data to the end of the column by setting
-- @Offset to NULL.
UPDATE Production.Document
SET DocumentSummary .WRITE (N' Appending data to the end of the column.', NULL, 0)
WHERE Title = N'Crank Arm and Tire Maintenance';
SELECT DocumentSummary
FROM Production.Document
WHERE Title = N'Crank Arm and Tire Maintenance';
-- Removing all data from @Offset to the end of the existing value by
-- setting expression to NULL.
UPDATE Production.Document
SET DocumentSummary .WRITE (NULL, 56, 0)
WHERE Title = N'Crank Arm and Tire Maintenance';
SELECT DocumentSummary
FROM Production.Document
WHERE Title = N'Crank Arm and Tire Maintenance';
-- Removing partial data beginning at position 9 and ending at
-- position 21.
UPDATE Production.Document
SET DocumentSummary .WRITE ('',9, 12)
WHERE Title = N'Crank Arm and Tire Maintenance';
SELECT DocumentSummary
FROM Production.Document
WHERE Title = N'Crank Arm and Tire Maintenance';
T. 使用包含 OPENROWSET 的 UPDATE 修改 varbinary(max) 列
以下示例将 varbinary(max) 列中存储的现有图像替换为新图像。 将 OPENROWSET 函数和 BULK 选项一起使用以将图像加载到列中。 此示例假定指定的文件路径中存在名为 Tires.jpg
的文件。
USE AdventureWorks2022;
UPDATE Production.ProductPhoto
SET ThumbNailPhoto = (
SELECT *
FROM OPENROWSET(BULK 'c:Tires.jpg', SINGLE_BLOB) AS x )
WHERE ProductPhotoID = 1;
U. 使用 UPDATE 来修改 FILESTREAM 数据
以下示例使用 UPDATE 语句来修改文件系统文件中的数据。 我们不建议使用此方法将大量数据以流方式传输到文件。 请使用适当的 Win32 接口。 下面的示例将文件记录中的所有文本替换为文本 Xray 1
。 有关详细信息,请参阅 FILESTREAM (SQL Server)。
UPDATE Archive.dbo.Records
SET [Chart] = CAST('Xray 1' as VARBINARY(max))
WHERE [SerialNumber] = 2;
更新用户定义类型
以下示例修改 CLR 用户定义类型 (UDT) 列中的值。 演示了三种方法。 有关用户定义的列的详细信息,请参阅 CLR 用户定义类型。
V. 使用系统数据类型
通过提供 SQL Server 系统数据类型的值可以更新 UDT,条件是该用户定义类型支持该类型的隐式转换或显式转换。 以下示例显示如何通过从字符串显式转换来更新用户定义类型 Point
的列中的值。
UPDATE dbo.Cities
SET Location = CONVERT(Point, '12.3:46.2')
WHERE Name = 'Anchorage';
W. 调用方法
通过调用标记为“赋值函数”的用户定义类型的方法执行更新,可以更新 UDT。 以下示例调用类型 Point
的名为 SetXY
的赋值函数方法。 这将更新该类型的实例状态。
UPDATE dbo.Cities
SET Location.SetXY(23.5, 23.5)
WHERE Name = 'Anchorage';
X. 修改属性或数据成员的值
通过修改用户定义类型的已注册属性或公共数据成员的值,可以更新 UDT。 提供值的表达式必须可隐式转换为属性的类型。 以下示例修改用户定义类型 X
的属性 Point
的值。
UPDATE dbo.Cities
SET Location.X = 23.5
WHERE Name = 'Anchorage';
通过使用提示覆盖查询优化器的默认行为
本节中的示例说明了如何使用表提示和查询提示在处理 UPDATE 语句时暂时覆盖查询优化器的默认行为。
由于 SQL Server 查询优化器通常会为查询选择最佳执行计划,因此我们建议仅在最后迫不得已的情况下才可由资深的开发人员和数据库管理员使用提示。
Y. 指定表提示
以下示例指定 表提示 TABLOCK。 此提示指定对 Production.Product
表采用共享锁,并保持到 UPDATE 语句结束。
USE AdventureWorks2022;
UPDATE Production.Product
WITH (TABLOCK)
SET ListPrice = ListPrice * 1.10
WHERE ProductNumber LIKE 'BK-%';
的授权请求。 指定查询提示
以下示例在 UPDATE 语句中指定查询提示OPTIMIZE FOR (@variable)
。 此提示指示查询优化器在编译和优化查询时对局部变量使用特定值。 仅在查询优化期间使用该值,在查询执行期间不使用该值。
USE AdventureWorks2022;
CREATE PROCEDURE Production.uspProductUpdate
@Product NVARCHAR(25)
SET NOCOUNT ON;
UPDATE Production.Product
SET ListPrice = ListPrice * 1.10
WHERE ProductNumber LIKE @Product
OPTION (OPTIMIZE FOR (@Product = 'BK-%') );
-- Execute the stored procedure
EXEC Production.uspProductUpdate 'BK-%';
捕获 UPDATE 语句的结果
本节中的示例说明如何使用 OUTPUT 子句从 UPDATE 语句影响的每一行返回信息(或基于的表达式)。 这些结果可以返回到处理应用程序,以供在确认消息、存档以及其他类似的应用程序要求中使用。
AA. 使用包含 OUTPUT 子句的 UPDATE
以下示例针对少于 10 VacationHours 的员工将 Employee
表中的列 VacationHours
更新 25% 并将 ModifiedDate
列中的值设置为当前日期。 OUTPUT
子句将返回 VacationHours
的值,该值在将 UPDATE
列中的 deleted.VacationHours
语句和 inserted.VacationHours
列中的已更新值应用于 @MyTableVar
表变量之前存在。
在它后面的两个 SELECT
语句返回 @MyTableVar
中的值以及 Employee
表中更新操作的结果。 有关使用 OUTPUT 子句的更多示例,请参阅 OUTPUT 子句 (Transact-SQL)。
USE AdventureWorks2022;
--Display the initial data of the table to be updated.
SELECT BusinessEntityID, VacationHours, ModifiedDate, HireDate
FROM HumanResources.Employee
WHERE VacationHours < 10
DECLARE @MyTableVar TABLE (
EmpID int NOT NULL,
OldVacationHours smallint,
NewVacationHours smallint,
ModifiedDate datetime);
UPDATE HumanResources.Employee
SET VacationHours = VacationHours * 1.25,
ModifiedDate = GETDATE()
OUTPUT inserted.BusinessEntityID,
deleted.VacationHours,
inserted.VacationHours,
inserted.ModifiedDate
INTO @MyTableVar
WHERE VacationHours < 10
--Display the result set of the table variable.
SELECT EmpID, OldVacationHours
, NewVacationHours, ModifiedDate
FROM @MyTableVar;
--Display the result set of the table.
SELECT BusinessEntityID, VacationHours, ModifiedDate, HireDate
FROM HumanResources.Employee
WHERE VacationHours < 10
在其他语句中使用 UPDATE
本节中的示例说明了如何在其他语句中使用 UPDATE。
AB. 在存储过程中使用 UPDATE
下面的示例在一个存储过程中使用了 UPDATE 语句。 该过程采用一个输入参数 @NewHours
和一个输出参数 @RowCount
。 在 UPDATE 语句中使用 @NewHours
参数值来更新表 HumanResources.Employee
中的列 VacationHours
。 @RowCount
输出参数用于将影响的行数返回给一个局部变量。 在 SET 子句中使用 CASE 表达式,以便确定按条件确定为 VacationHours
设置的值。 在按每小时向员工付薪时 (SalariedFlag
= 0),VacationHours
设置为当前小时数加上 @NewHours
中指定的值;否则,VacationHours
设置为在 @NewHours
中指定的值。
USE AdventureWorks2022;
CREATE PROCEDURE HumanResources.Update_VacationHours
@NewHours SMALLINT
SET NOCOUNT ON;
UPDATE HumanResources.Employee
SET VacationHours =
( CASE
WHEN SalariedFlag = 0 THEN VacationHours + @NewHours
ELSE @NewHours
WHERE CurrentFlag = 1;
EXEC HumanResources.Update_VacationHours 40;
AC. 在 TRY…CATCH 块中使用 UPDATE
以下示例在 TRY…CATCH 块中使用 UPDATE 语句来处理在执行更新操作期间可能发生的执行错误。
USE AdventureWorks2022;
BEGIN TRANSACTION;
BEGIN TRY
-- Intentionally generate a constraint violation error.
UPDATE HumanResources.Department
SET Name = N'MyNewName'
WHERE DepartmentID BETWEEN 1 AND 2;
END TRY
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION;
END CATCH;
IF @@TRANCOUNT > 0
COMMIT TRANSACTION;
示例:Azure Synapse Analytics 和 Analytics Platform System (PDW)
AD. 使用简单 UPDATE 语句
以下示例显示在未使用 WHERE 子句指定要更新的行时如何影响所有行。
此示例针对 DimEmployee
表中的所有行更新 EndDate
和 CurrentFlag
列中的值。
-- Uses AdventureWorks
UPDATE DimEmployee
SET EndDate = '2010-12-31', CurrentFlag='False';
也可以在 UPDATE 语句中使用计算值。 下面的示例将 ListPrice
表中所有行的 Product
列的值加倍。
-- Uses AdventureWorks
UPDATE DimEmployee
SET BaseRate = BaseRate * 2;
AE. 将 UPDATE 语句用于 WHERE 子句
以下示例使用 WHERE 子句指定要更新的行。
-- Uses AdventureWorks
UPDATE DimEmployee
SET FirstName = 'Gail'
WHERE EmployeeKey = 500;
AF. 将 UPDATE 语句用于标签
以下示例演示 UPDATE 语句中 LABEL 的用法。
-- Uses AdventureWorks
UPDATE DimProduct
SET ProductSubcategoryKey = 2
WHERE ProductKey = 313
OPTION (LABEL = N'label1');
此示例创建一个表,用于存储每年的总销售额。 通过对 FactInternetSales 表运行 SELECT 语句来更新 2004 年的总销售额。
-- Uses AdventureWorks
CREATE TABLE YearlyTotalSales (
YearlySalesAmount MONEY NOT NULL,
Year SMALLINT NOT NULL )
WITH ( DISTRIBUTION = REPLICATE );
INSERT INTO YearlyTotalSales VALUES (0, 2004);
INSERT INTO YearlyTotalSales VALUES (0, 2005);
INSERT INTO YearlyTotalSales VALUES (0, 2006);
UPDATE YearlyTotalSales
SET YearlySalesAmount=
(SELECT SUM(SalesAmount) FROM FactInternetSales WHERE OrderDateKey >=20040000 AND OrderDateKey < 20050000)
WHERE Year=2004;
SELECT * FROM YearlyTotalSales;
AH. 更新语句的 ANSI 联接
此示例演示了如何根据与其他表联接的结果来更新数据。
CREATE TABLE dbo.Table1
(ColA INT NOT NULL, ColB DECIMAL(10,3) NOT NULL);
CREATE TABLE dbo.Table2
(ColA INT NOT NULL, ColB DECIMAL(10,3) NOT NULL);
INSERT INTO dbo.Table1 VALUES(1, 10.0);
INSERT INTO dbo.Table2 VALUES(1, 0.0);
UPDATE dbo.Table2
SET dbo.Table2.ColB = dbo.Table2.ColB + dbo.Table1.ColB
FROM dbo.Table2
INNER JOIN dbo.Table1
ON (dbo.Table2.ColA = dbo.Table1.ColA);
SELECT ColA, ColB
FROM dbo.Table2;
CREATE TABLE (Transact-SQL)
CREATE TRIGGER (Transact-SQL)
游标 (Transact-SQL)
DELETE (Transact-SQL)
INSERT (Transact-SQL)
文本与图像函数 (Transact-SQL)
WITH common_table_expression (Transact-SQL)
FILESTREAM (SQL Server)
排序规则和 Unicode 支持
单字节和多字节字符集
即将推出:在整个 2024 年,我们将逐步取消以“GitHub 问题”作为内容的反馈机制,并将其替换为新的反馈系统。 有关详细信息,请参阅:https://aka.ms/ContentUserFeedback。
提交和查看相关反馈