SQL Server更新表中数据

通过使用 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
    原文作者:zoyoto
    原文地址: https://www.jianshu.com/p/d623d75c3239
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞