通过使用 UPDATE 更改数据
UPDATE 语句可以更改表或视图中单行、行组或所有行的数据值。还可以用该语句更新远程服务器上的行,前提是用来访问远程服务器的 OLE DB 访问接口支持更新操作。引用某个表或视图的 UPDATE 语句每次只能更改一个基表中的数据。
UPDATE 语法的简化形式为:
UPDATE table_or_view
SET column_name1 = expression1,column_name2 = expression2...
FROM table_sources
WHERE search_condition
- table_or_view 指定要从中更新行的表或视图。table_or_view 中所有符合 WHERE 搜索条件的行都将被更新。如果没有指定 WHERE 子句,将更新 table_or_view 中的所有行。
- FROM 子句指定可由 WHERE 子句搜索条件中的谓词使用的其他表或视图及联接条件(以关联表为条件更新另一条的数据),以限定要从 table_or_view 中更新的行。不会从 FROM 子句指定的表中更新行,只从 table_or_view 指定的表中更新行。
- SET包含要更新的列和每个列的新值的列表(用逗号分隔),格式为 column_name = expression。表达式提供的值包含多个项目,如常量、从其他表或视图的列中选择的值或使用复杂的表达式计算出来的值。
日志记录行为
UPDATE 语句将完全记入日志。但是,如果将数据库恢复模式设置为大容量日志恢复模式或简单模式,则对插入或追加新数据的 .WRITE 更新进行最小日志记录。在使用 .WRITE 更新现有值时,不使用最小日志记录。
【示例】
(1)以下示例修改 SalesPerson 表中的 SalesYTD 列,以反映 SalesOrderHeader 表中记录的最近销售情况。
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);
GO
使用 TOP 限制更新的行
使用 TOP 子句来限制 UPDATE 语句中修改的行数。在与 UPDATE语句结合使用的 TOP(n) 表达式中引用的行不按任何顺序排列,将针对随机选择的 n 行执行更新操作。
以下示例按照 Employee 表中 10 个随机行的 25% 更新 VacationHours 列。
UPDATE TOP (10) HumanResources.Employee SET VacationHours = VacationHours * 1.25
如果需要使用 TOP 来应用按有意义的时间顺序排列的更新,您必须同时使用 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;
GO
通过使用游标更改数据
ADO、OLE DB 和 ODBC API 支持更新结果集中应用程序所在的当前行。以下步骤说明了基本的过程:
1.将结果集列与程序变量绑定。
2.执行查询。
3.调用 API 函数或方法,将应用程序定位到结果集的某一行。
4.对于要更新的任何列,使用新数据值填充绑定程序变量。
5.调用下列一个函数或一种方法来插入行:
- 在 ADO 中,调用 **Recordset **对象的 Update 方法。
- 在 OLE DB 中,调用 IRowsetChange 接口的 SetData 方法。
- 在 ODBC 中,调用带有 SQL_UPDATE 选项的 SQLSetPos 函数。
如果您使用的是 Transact-SQL 服务器游标,则可以通过使用包含 WHERE CURRENT OF子句的 UPDATE 语句更新当前行。使用此子句所做的更改只影响游标所在的行。如果游标基于某个联接,则只修改 UPDATE 语句中指定的 table_name。而不影响其他参与该游标的表。
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;
GO
UPDATE (Transact-SQL)
[ WITH <common_table_expression> [...n] ]
UPDATE
[ TOP ( expression ) [ PERCENT ] ]
{ { table_alias | <object> | rowset_function_limited
[ WITH ( <Table_Hint_Limited> [ ...n ] ) ]
}
| @table_variable
}
SET
{ column_name = { expression | DEFAULT | NULL }
| { udt_column_name.{ { property_name = expression
| field_name = expression }
| method_name ( argument [ ,...n ] )
}
}
| column_name { .WRITE ( expression , @Offset , @Length ) }
| @variable = expression
| @variable = column = expression
| column_name { += | -= | *= | /= | %= | &= | ^= | |= }expression
| @variable { += | -= | *= | /= | %= | &= | ^= | |= }expression
| @variable = column { += | -= | *= | /= | %= | &= | ^= | |= }expression
} [ ,...n ]
[ <OUTPUT Clause> ]
[ FROM { <table_source> } [ ,...n ] ]
[ WHERE { <search_condition>
| { [ CURRENT OF
{ { [ GLOBAL ] cursor_name }
| cursor_variable_name
}
]
}
}
]
[ OPTION ( <query_hint> [ ,...n ] ) ]
[ ; ]
<object> ::=
{
[ server_name . database_name . schema_name .
| database_name .[ schema_name ] .
| schema_name .
]
table_or_view_name}
- TOP ( expression) [ PERCENT ]
指定将要更新的行数或行百分比。expression 可以为行数或行百分比。 - table_alias:在表示要从中更新行的表或视图的 FROM 子句中指定的别名。
- server_name:表或视图所在的链接服务器的名称。
- database_name:数据库的名称。
- schema_name:该表或视图所属架构的名称。
- table_or view_name:要更新行的表或视图的名称。
- @table_variable:将表变量指定为表源。
- SET:指定要更新的列或变量名称的列表。
- column_name
包含要更改的数据的列。column_name 必须已存在于 table_or view_name 中。不能更新标识列。 - DEFAULT
指定用为列定义的默认值替换列中的现有值。如果该列没有默认值并且定义为允许 Null 值,则该参数也可用于将列更改为 NULL。 - udt_column_name:用户定义类型列。
- property_name | field_name:用户定义类型的公共属性或公共数据成员。
- method_name(argument [ ,...n] ):带一个或多个参数的 udt_column_name 的非静态公共赋值函数方法。
- .WRITE (expression,@Offset,@Length)
- 指定修改 column_name 值的一部分。从 column_name 的 @Offset 开始,一共@Length个单位。
- @Offset 是 column_name 值中的起点,数据类型为 bigint,不能为负数。如果 @Offset 为 NULL,则更新操作将在现有 column_name 值的结尾追加 expression,并忽略 @Length。如果 @Offset 大于 column_name 值的长度,则数据库引擎将返回错误。如果 @Offset 加上 @Length 超出了列中基础值的限度,则将删除到值的最后一个字符。如果 @Offset 加上 LEN(expression) 大于声明的基础大小,则将出现错误。
- @Length 是指列中某个部分的长度,从 @Offset 开始,该长度由 expression 替换。@Length 的数据类型为 bigint,不能为负数。如果 @Length 为 NULL,则更新操作将删除从 @Offset 到 column_name 值的结尾的所有数据。
- expression 是复制到 column_name 的值。expression 必须运算或隐式转换为 column_name 类型。如果将 expression 设置为 NULL,则忽略@Length,并将 column_name 中的值按指定的 @Offset 截断。
- 只有 varchar(max)、nvarchar(max) 或 varbinary(max) 列才能使用此子句来指定。
- column_name 不能为 NULL,也不能由表名或表别名限定。
- @variable
- 已声明的变量,该变量将设置为 expression 所返回的值。
- SET @variable = column = expression 将变量设置为与列相同的值。这与 SET @variable = column, column = expression 不同,后者将变量设置为列更新前的值。
- <OUTPUT_Clause>
在 UPDATE 操作中,返回更新后的数据或基于更新后的数据的表达式。针对远程表或视图的任何 DML 语句都不支持 OUTPUT 子句。 - cursor_name
要从中进行提取的开放游标的名称。如果同时存在名为 cursor_name 的全局游标和局部游标,那么,在指定了 GLOBAL 时,该参数是指全局游标;否则是指局部游标。游标必须允许更新。 - OPTION ( <query_hint> [ ,... n] ):指定优化器提示用于自定义数据库引擎处理语句的方式。
指定 FROM 子句为更新操作提供条件时务须小心。如果 UPDATE 语句包含的 FROM 子句在指定后并未生成对每个更新的列只有一个值可用的结果(换句话说,如果 UPDATE 语句是不确定性的),则其结果将不明确。这可能导致意外结果。例如,对于下面脚本中的 UPDATE 语句,Table1 中的全部两行都满足 UPDATE 语句中 FROM 子句的限定条件;但是,将使用 Table1 的哪一行来更新 Table2 中的行是不明确的。
-- Table1有两行的ColA都与Table2的ColA相等
INSERT INTO dbo.Table1 VALUES(1, 10.0), (1, 20.0);
INSERT INTO dbo.Table2 VALUES(1, 0.0);
GO
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);
GO
SELECT ColA, ColB
FROM dbo.Table2;
当结合使用 FROM 和 WHERE CURRENT OF 子句时,可能发生同样的问题。在以下示例中,Table2 中的全部两行都满足 UPDATE 语句中 FROM 子句的限定条件。将使用 Table2 的哪一行来更新 Table1 中的行是不明确的。
CREATE TABLE dbo.Table1
(c1 int PRIMARY KEY NOT NULL, c2 int NOT NULL);
GO
CREATE TABLE dbo.Table2
(d1 int PRIMARY KEY NOT NULL, d2 int NOT NULL);
GO
INSERT INTO dbo.Table1 VALUES (1, 10);
INSERT INTO dbo.Table2 VALUES (1, 20), (2, 30);
GO
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;
GO
【示例】
1、设置列值
A、指定复合运算符
以下示例使用变量 @NewPrice 通过在当前价格基础上加 10 来提高所有红色自行车的价格。
DECLARE @NewPrice int = 10;
UPDATE Production.Product SET ListPrice += @NewPrice WHERE Color = N'Red';
以下示例使用复合运算符 += 针对 ScrapReasonID 为 10 到 12 的行将数据 ' - tool malfunction' 追加到列 Name 中的现有值之后。
UPDATE Production.ScrapReason
SET Name += ' - tool malfunction'
WHERE ScrapReasonID BETWEEN 10 and 12;
B. 在 SET 子句中指定子查询
以下示例使用 SET 子句中的子查询来确定用于更新列的值。子查询必须只返回标量值(即每行返回一个值)。
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);
GO
C. 使用 DEFAULT 值更新行
UPDATE Production.Location
SET CostRate = DEFAULT
WHERE CostRate > 20.00
2、指定除标准表之外的目标对象
A. 将视图指定为目标对象
以下示例通过将视图指定为目标对象来更新表中的行。该视图定义引用了多个表,但是 UPDATE 语句成功运行,因为它只引用了某个基础表中的列。如果指定两个表中的列,UPDATE 语句将失败。
UPDATE Person.vStateProvinceCountryRegion
SET CountryRegionName = 'United States of America'
WHERE CountryRegionName = 'United States';
B. 将表别名指定为目标对象
以下示例将更新 Production.ScrapReason 表中的行。将分配给 FROM子句中 ScrapReason 的表别名指定为 UPDATE 子句中的目标对象。
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;
C. 将表变量指定为目标对象
DECLARE @MyTableVar table(
EmpID int NOT NULL,
NewVacationHours int,
ModifiedDate datetime);
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;
GO
3、更新大型对象数据类型
A. 使用包含 .WRITE 的 UPDATE 来修改 nvarchar(max) 列中的数据
以下示例使用 .WRITE 子句更新DocumentSummary(Production.Document 表内的 nvarchar(max) 列)中的部分值。通过指定替换单词、现有数据中要替换的单词的开始位置(偏移量)以及要替换的字符数(长度),将单词 components 替换为单词 features。
UPDATE Production.Document
SET DocumentSummary .WRITE (N'features',28,10)
WHERE Title = N'Front Reflector Bracket Installation';
GO
B. 使用包含 .WRITE 的 UPDATE 在 nvarchar(max) 列中添加和删除数据
以下示例从当前值设置为 NULL 的 nvarchar(max) 列中添加和删除列。由于不能使用 .WRITE 子句修改 NULL 列,因此先使用临时数据填充该列。然后,使用 .WRITE 子句将该数据替换为正确的数据。
其他示例将数据追加到列值的结尾,从列中删除(截断)数据,最后从列中删除部分数据。
-- Replacing NULL value with temporary data.
UPDATE Production.Document
SET DocumentSummary = N'Replacing NULL value'
WHERE Title = N'Crank Arm and Tire Maintenance';
GO
-- 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 the tires.',0,NULL)
WHERE Title = N'Crank Arm and Tire Maintenance';
GO
-- Appending additional data to the end of the column by setting
UPDATE Production.Document
SET DocumentSummary .WRITE (N' Appending data.', NULL, 0)
WHERE Title = N'Crank Arm and Tire Maintenance';
GO
-- Removing all data from @Offset to the end of the existing value by
UPDATE Production.Document
SET DocumentSummary .WRITE (NULL, 56, 0)
WHERE Title = N'Crank Arm and Tire Maintenance';
GO
-- 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';
GO
4、捕获 UPDATE 语句的结果
A. 使用包含 OUTPUT 子句的 UPDATE
DECLARE @MyTableVar table(
EmpID int NOT NULL,
OldVacationHours int,
NewVacationHours int,
ModifiedDate datetime);
UPDATE TOP (10) HumanResources.Employee
SET VacationHours = VacationHours * 1.25,ModifiedDate = GETDATE()
OUTPUT inserted.BusinessEntityID,
deleted.VacationHours,
inserted.VacationHours,
inserted.ModifiedDate
INTO @MyTableVar;
GO