UPDATE 语句不仅指定要更改的行,还指定要用作这些行中特定列的新值的表达式。
可以使用 UPDATE 语句更改表中的单个行、一组行或所有行。与其它数据修改语句(INSERT、MERGE 和 DELETE)不同,UPDATE 语句还可以同时对多个表中的行进行修改。在所有情况下,UPDATE 语句的执行均为原子操作;要么正确无误地修改所有行,要么不修改任何行。例如,如果当前修改的一个值的数据类型不正确,或者在新值导致违反 CHECK 约束时,UPDATE 将失败,并且整个操作都会回退。
UPDATE 语句语法的简化版本如下:
UPDATE table-name SET column_name = expression WHERE search-condition
如果 Newton Ent. 公司(在 SQL Anywhere 示例数据库的 Customers 表中)被 Einstein, Inc. 接管,则可以使用如下语句更新该公司的名称:
UPDATE Customers SET CompanyName = 'Einstein, Inc.' WHERE CompanyName = 'Newton Ent.'; |
可以在 WHERE 子句中使用任何表达式。如果不确定公司名称是如何拼写的,则可以使用如下所示的语句尝试更新名为 Newton 的任何公司:
UPDATE Customers SET CompanyName = 'Einstein, Inc.' WHERE CompanyName LIKE 'Newton%'; |
搜索条件不需要引用要更新的列。Newton Entertainments 的公司 ID 是 109。由于 ID 值是表的主键,所以可以确定使用以下语句更新正确的行:
UPDATE Customers SET CompanyName = 'Einstein, Inc.' WHERE ID = 109; |
还可以根据 Interactive SQL 中的结果集修改行。
SET 子句指定要更新的列及其新值。WHERE 子句确定将被更新的行。如果不具有 WHERE 子句,则将用 SET 子句中提供的值更新所有行中指定的列。
在 SET 子句中指定的表达式可以是常量文字、主机或 SQL 变量、子查询、特殊值(如 CURRENT TIMESTAMP)、从另一个表中取出的表达式值,或者是上述对象的任意组合。您还可以在 SET 子句中指定 DEFAULT 以表示基表列的缺省值。如果表达式的数据类型与待修改列的数据类型不同,则数据库服务器会自动将表达式转换为列的类型(如果可能)。如果无法转换,将出现数据异常,并且 UPDATE 语句将失败。
除了可以修改列值外,还可以使用 SET 子句设置变量值。以下示例不仅更新表 T,还为变量 @var 赋值:
UPDATE T SET @var = expression1, col1 = expression2 WHERE...; |
此过程相当于相继执行了 SELECT 语句和 UPDATE 语句:
SELECT @var = expression1 FROM T WHERE... ; UPDATE T SET col1 = expression2 WHERE...; |
在 UPDATE 语句中为变量赋值的优点在于,可以在持有写锁定的同时在该语句执行期间设置变量值,这可防止因来自其它连接的并发更新活动而分配意外值。
WHERE 子句通过将 search-condition 应用于 UPDATE 语句中指定的表或表表达式笛卡尔乘积来指定要更新的行。例如,以下语句用 "Extra Large" T 恤衫替换 "One Size Fits All" T 恤衫。
UPDATE Products SET Size = 'Extra Large' WHERE Name = 'Tee Shirt' AND Size = 'One Size Fits All'; |
可使用形式更复杂的 UPDATE 语句来对关联和其它类型的表表达式进行更新。
例如,UPDATE 语句的语法 1 为:
UPDATE [ row-limitation ] table-name SET set-item[, ...] FROM table-expression [, ...] ] [ WHERE search-condition ] [ ORDER BY expression [ ASC | DESC ] , ...] [ OPTION( query-hint, ... ) ]
这种形式的 UPDATE 语句的语义是:首先计算一个由来自每个 table-expression 的所有行组合构成的结果集,然后在 WHERE 子句中应用 search-condition,最后使用 ORDER BY 子句对生成的行进行排序。 此计算的结果是一组待修改的行。每个 table-expression 都可以由基表、视图和派生表的关联组成。该语法允许使用其它表中的列值对一个或多个表进行更新。查询优化程序可能会对操作重新排序,以便为 UPDATE 语句创建更加高效的执行策略。
如果某个基表行在要修改的一组行中多次出现,并且该基表行的新值因每次尝试修改而有所不同,则会多次更新该行。如果存在 BEFORE ROW UPDATE 触发器,则每次修改单个行都会触发 BEFORE ROW UPDATE 触发器,但会受到该触发器的 UPDATE OF column-list 子句的制约。根据 AFTER ROW UPDATE 触发器的 UPDATE OF column-list 子句,每次修改行时也会触发 AFTER ROW UPDATE 触发器,但仅限行值实际发生更改的情况。
根据触发器类型以及包含每个触发器定义的 ORDER 子句的值,为每个更新的表触发触发器。但是,如果 UPDATE 语句修改多个表,则无法保证表的更新顺序。
以下示例为 Products 表创建一个 BEFORE ROW UPDATE 触发器和一个 AFTER STATEMENT UPDATE 触发器,这两个触发器都会在数据库服务器消息窗口中输出一条消息:
CREATE OR REPLACE TRIGGER trigger0 BEFORE UPDATE ON Products REFERENCING OLD AS old_product NEW AS new_product FOR EACH ROW BEGIN PRINT ('BEFORE row: PK value: ' || old_product.ID || ' New Price: ' || new_product.UnitPrice ); END; CREATE OR REPLACE TRIGGER trigger1 AFTER UPDATE ON Products REFERENCING NEW AS new_product FOR EACH STATEMENT BEGIN DECLARE @pk INTEGER; DECLARE @newUnitPrice DECIMAL(12,2); DECLARE @err_notfound EXCEPTION FOR SQLSTATE VALUE '02000'; DECLARE new_curs CURSOR FOR SELECT ID, UnitPrice FROM new_product; OPEN new_curs; LoopGetRow: LOOP FETCH NEXT new_curs INTO @pk, @newUnitPrice; IF SQLSTATE = @err_notfound THEN LEAVE LoopGetRow END IF; PRINT ('AFTER stmt: PK value: ' || @pk || ' Unit price: ' || @newUnitPrice ); END LOOP LoopGetRow; CLOSE new_curs END; |
假设您随后对 Products 表与 SalesOrderItems 表的连接执行 UPDATE 语句,以便将自 2001 年 4 月 1 日起交付且至少具有一个大订单的产品打九五折:
UPDATE Products p JOIN SalesOrderItems s ON (p.ID = s.ProductID) SET p.UnitPrice = p.UnitPrice * 0.95 WHERE s.ShipDate > '2001-04-01' AND s.Quantity >= 72; |
数据库服务器消息窗口显示下列消息:
BEFORE row: PK value: 700 New Price: 14.25 BEFORE row: PK value: 302 New Price: 13.30 BEFORE row: PK value: 700 New Price: 13.54 AFTER stmt: PK value: 700 Unit price: 14.25 AFTER stmt: PK value: 302 Unit price: 13.30 AFTER stmt: PK value: 700 Unit price: 13.54 |
此消息表示 Product 700 已更新两次,因为 Product 700 包含在与 UPDATE 语句中的搜索条件匹配的两个不同订单中。重复更新对 BEFORE ROW 触发器和 AFTER STATEMENT 触发器均可见。每次修改行时,每个触发器调用对应的 OLD 值和 NEW 值都会相应地更改。使用 AFTER STATEMENT 触发器时,由 REFERENCING 子句构成的临时表中的行顺序可能与已修改行的顺序不一致,因此无法保证这些行的准确顺序。
由于重复执行更新操作,因此 Product 700 的 UnitPrice 打了两次折,即从最初的 $15.00 降至 $13.54(让出了 9.75 的折扣)而不是 $14.25。为避免出现这种意外的结果,您可以用公式来表示 UPDATE 语句,以使用 EXISTS 子查询(而非连接)来保证最多对每个 Product 行修改一次。重写的 UPDATE 语句同时使用 EXISTS 子查询以及允许采用 FROM 子句的替代 UPDATE 语句语法:
UPDATE Products AS p SET p.UnitPrice = p.UnitPrice * 0.95 FROM Products AS p WHERE EXISTS( SELECT * FROM SalesOrderItems s WHERE p.ID = s.ProductID AND s.ShipDate > '2001-04-01' AND s.Quantity >= 72); |
如果 UPDATE 语句在执行期间违反参照完整性均束,则该语句的行为将由 wait_for_commit 选项的设置加以控制。如果将 wait_for_commit 选项设置为 Off,并且出现了参照约束违规,系统将立即自动回退 UPDATE 语句的影响并显示一条错误消息。如果将 wait_for_commit 选项设置为 On,则暂时忽略由 UPDATE 语句导致的任何参照完整性约束违规,而在连接执行 COMMIT 操作时检查这些约束违规。
如果当前修改的一个或多个基表具有主键、UNIQUE 约束或唯一索引,则逐行执行 UPDATE 语句可能会违反唯一性约束。 例如,您可以发出一个 UPDATE 语句,用来递增表 T 的所有主键列值:
UPDATE T SET PKcol = PKcol + 1; |
如果在执行 UPDATE 语句时出现唯一性违规,则数据库服务器将自动执行以下操作:
将已修改行的旧值和新值复制到一个与当前修改的基表具有相同模式的临时表中。
从基表中删除原始行。完成此删除操作后,不会触发任何 DELETE 触发器。
在 UPDATE 语句的执行过程中,哪些行会成功更新以及哪些行会被临时删除要视计算顺序而定,无法得到保证。临时删除的这些行可能会对在较低隔离级别(隔离级别 0、1 或 2)执行的其它连接发出的 SQL 请求行为产生影响。已修改表的任何 BEFORE ROW 或 AFTER ROW 触发器都会收到按照触发器的 REFERENCING 子句传递的各行的新值和旧值,但如果 ROW 触发器对已修改表发出单独的 SQL 语句,临时表中保存的行将丢失。
UPDATE 语句完成对各行的修改操作后,临时表中保存的行将被插回到基表中。如果仍存在唯一性违规,将回退整个 UPDATE 语句。仅当成功将临时表中保存的所有行重新插入基表后,才会触发任意 AFTER STATEMENT 触发器。
如果当前修改的基表是参照完整性约束操作(包括 ON DELETE CASCADE、ON DELETE SET NULL、ON DELETE DEFAULT、ON UPDATE CASCADE、ON UPDATE SET NULL 和 ON UPDATE DEFAULT)的目标,则数据库服务器不会使用保存的表来临时存储行。
![]() |
使用DocCommentXchange讨论此页。
|
版权 © 2012, iAnywhere Solutions, Inc. - SQL Anywhere 12.0.1 |