MySQL的SQL语句 - 数据操作语句(15)- UPDATE 语句

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL的SQL语句 - 数据操作语句(15)- UPDATE 语句相关的知识,希望对你有一定的参考价值。

UPDATE 语句

UPDATE 是修改表中行的 DML 语句。

UPDATE 语句可以用 WITH 子句开头,定义在 UPDATE 中可访问的公共表表达式。

单表语法:

1. UPDATE [LOW_PRIORITY] [IGNORE] table_reference
2.     SET assignment_list
3.     [WHERE where_condition]
4.     [ORDER BY ...]
5.     [LIMIT row_count]
6. 
7. value:
8.     {expr | DEFAULT}
9. 
10. assignment:
11.     col_name = value
12. 
13. assignment_list:
14.     assignment [, assignment] ... 

多表语法:

1. UPDATE [LOW_PRIORITY] [IGNORE] table_references
2.     SET assignment_list
3.     [WHERE where_condition]

对于单表语法,UPDATE 语句用新值更新命名表中现有行的列。SET 子句指示要修改的列及其应给定的值。每个值可以用表达式给定,也可以使用关键字 DEFAULT 将列显式设置为其默认值。WHERE 子句(如果给定)指定要更新哪些行。如果没有 WHERE 子句,所有行都将更新。如果指定了 ORDER BY 子句,则按指定的顺序更新行。LIMIT 子句对可以更新的行数进行了限制。

对于多表语法,UPDATE 更新 table_references 中每个表中满足条件的行。每个匹配的行都会更新一次,即使它与条件匹配多次。对于多表语法,不能使用 ORDER BY 和 LIMIT。

对于分区表,此语句的单表和多表形式都支持使用 PARTITION 选项用作表引用的一部分。此选项接受分区或子分区列表。只检查列出的分区(或子分区)是否匹配,不在这些分区或子分区中的行不会更新,无论它是否满足 where_condition 条件。

注意

与在 INSERT 或 REPLACE 语句中使用 PARTITION 的情况不同,即使列出的分区(或子分区)中没有与 where_condition 匹配的行,UPDATE ... PARTITION 语句也被认为是成功的。

where_condition 是一个表达式,要更新的每一行都必须满足此表达式的条件。

只需要拥有在 UPDATE 语句实际更新引用的列的 UPDATE 权限。对于任何已读取但未修改的列,只需要 SELECT 权限。

UPDATE 语句支持以下修饰符:

● 使用 LOW_PRIORITY 修饰符,UPDATE 的执行将被延迟,直到没有其他客户端从表中读取数据。这只影响只使用表级锁定的存储引擎(如 MyISAM、MEMORY 和 MERGE)。

● 使用 IGNORE 修饰符,即使在更新过程中发生错误,更新语句也不会中止。不会更新在唯一键值上引发重复键冲突的行。可能导致数据转换错误的值的行将更新为最接近的有效值。

包括 ORDER BY 子句的 UPDATE IGNORE 语句被标记为不安全的基于语句的复制。(这是因为行的更新顺序决定了哪些行被忽略。)当使用基于语句的模式时,这些语句在错误日志中生成警告,在使用 MIXED 模式时,这些语句将使用基于行的格式写入二进制日志。

如果从要在表达式中更新的表中访问列,则 UPDATE 将使用该列的当前值。例如,下面的语句将 col1 设置为比当前值多1:

1. UPDATE t1 SET col1 = col1 + 1;

下面语句中的第二个赋值将 col2 设置为当前(更新的)col1 值,而不是原始 col1 值。结果是 col1 和 col2 的值相同。此行为与标准 SQL 不同。

1. UPDATE t1 SET col1 = col1 + 1, col2 = col1;

单表 UPDATE 分配通常从左到右进行计算。对于多表更新,不能保证以任何特定的顺序执行分配。

如果将列设置为当前的值,mysql 会注意到这一点,并且不会更新它。

如果把已声明为 NOT NULL 的列设置为 NULL,则在启用了严格 SQL 模式会出错;否则,该列将设置为列数据类型的隐式默认值,并且警告计数将递增。对于数值类型,隐式默认值为0;对于字符串类型,隐式默认值为空字符串(‘‘),对于日期和时间类型,默认值为“零”。

如果显式更新生成列,则唯一允许的值是 DEFAULT。

UPDATE 返回实际更改的行数。mysql_info() C API 函数返回匹配和更新的行数以及更新过程中出现的警告数。

可以使用 LIMIT row_count 来限制 UPDATE 的范围。LIMIT 子句是匹配行的限制。只要找到满足 WHERE 子句的 row_count 行,语句就会立即停止,而不管这些行是否实际被更改。

如果 UPDATE 语句包含 ORDER BY 子句,则按该子句指定的顺序更新行。这在某些可能导致错误的情况下非常有用。假设表 t 包含一个具有唯一索引的列 id。以下语句可能会出现重复键错误而失败,这取决于行的更新顺序:

1. UPDATE t SET id = id + 1;

例如,如果表在 id 列中包含值 1 和 2,并且在 2 更新为 3 之前 1 先更新为2,则会发生错误。若要避免此问题,请添加 ORDER BY 子句,使 id 值较大的行在值较小的行之前更新:

1. UPDATE t SET id = id + 1 ORDER BY id DESC;

还可以执行覆盖多个表的 UPDATE 操作。但是,不能将 ORDER BY 或 LIMIT 用于多表更新。table_references 子句列出了连接中涉及的表。

1. UPDATE items,month SET items.price=month.price
2. WHERE items.id=month.id;

前面的示例显示了使用逗号运算符的内部联接,但多表更新语句可以使用 SELECT 语句中允许的任何类型的联接,例如 LEFT JOIN。

如果使用包含 InnoDB 表且有外键约束的多表 UPDATE 语句,那么 MySQL 优化器可能会按照与父/子关系不同的顺序处理表。在本例中,语句失败并回滚。相反,更新一个表并依赖 InnoDB 提供的 ON UPDATE 功能来相应地修改其他表。

不能在更新一个表的同时直接从子查询中对同一表进行选择。可以通过使用多表更新来解决此问题,其中一个表是从实际要更新的表派生的,并使用别名引用派生表。假设希望更新一个名为 items 的表,该表是使用以下语句定义的:

1. CREATE TABLE items (
2.     id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
3.     wholesale DECIMAL(6,2) NOT NULL DEFAULT 0.00,
4.     retail DECIMAL(6,2) NOT NULL DEFAULT 0.00,
5.     quantity BIGINT NOT NULL DEFAULT 0
6. );

若要降低利润为30%或更高并且库存少于100的商品的零售价,可以尝试使用如下所示的 UPDATE 语句,该语句在 WHERE 子句中使用子查询。如下所示,此语句不起作用:

1. mysql> UPDATE items
2.      > SET retail = retail * 0.9
3.      > WHERE id IN
4.      >     (SELECT id FROM items
5.      >         WHERE retail / wholesale >= 1.3 AND quantity > 100);
6. ERROR 1093 (HY000): You can‘t specify target table ‘items‘ for update in FROM clause

替代方法是可以使用多表更新,其中子查询被移动到要更新的表列表中,使用别名在最外层的 WHERE 子句中引用它,如下所示:

1. UPDATE items,
2.        (SELECT id FROM items
3.         WHERE id IN
4.             (SELECT id FROM items
5.              WHERE retail / wholesale >= 1.3 AND quantity < 100))
6.         AS discounted
7. SET items.retail = items.retail * 0.9
8. WHERE items.id = discounted.id;

因为默认情况下,优化器会尝试将派生表 discounted 合并到最外层的查询块中,只有在强制物化派生表时,这才有效。可以在运行更新之前将 optimizer_switch 系统变量的 derived_merge 标志设置为 off,或使用 NO_MERGE 优化器提示来执行此操作,如下所示:

1. UPDATE /*+ NO_MERGE(discounted) */ items,
2.        (SELECT id FROM items
3.         WHERE retail / wholesale >= 1.3 AND quantity < 100)
4.         AS discounted
5.     SET items.retail = items.retail * 0.9
6.     WHERE items.id = discounted.id;

在这种情况下使用优化器提示的好处是,它只适用于使用它的查询块中,因此在执行 UPDATE 之后,不必再次更改 optimizer_switch 的值。

另一种可能是重写子查询,使其不使用 IN 或 EXISTS,如下所示:

1. UPDATE items,
2.        (SELECT id, retail / wholesale AS markup, quantity FROM items)
3.        AS discounted
4.     SET items.retail = items.retail * 0.9
5.     WHERE discounted.markup >= 1.3
6.     AND discounted.quantity < 100
7.     AND items.id = discounted.id;

在这种情况下,子查询默认情况下是物化的,而不是合并的,因此不需要禁用派生表的合并。

官方网址:
https://dev.mysql.com/doc/refman/8.0/en/update.html

以上是关于MySQL的SQL语句 - 数据操作语句(15)- UPDATE 语句的主要内容,如果未能解决你的问题,请参考以下文章

MySQL语句 - sql语句

MySQL语句 - sql语句

MySQL的SQL语句 - 数据操作语句(12)- SELECT 语句

MySQL的SQL语句 - 数据操作语句(16)- VALUES 语句

MySQL的SQL语句 - 数据操作语句(14)- TABLE 语句

MySQL的SQL语句 - 数据操作语句(13)- 子查询