UPDATE
功能描述
更新表中的数据。UPDATE修改满足条件的所有行中指定的字段值,WHERE子句声明条件,SET子句指定的字段会被修改,没有出现的字段则保持原值。
注意事项
- 使用UPDATE请遵循UPDATE操作规范。
- 权限要求:要修改表,用户必须对该表有UPDATE权限,对expression或condition条件里涉及到的任何表要有SELECT权限。
- 不允许对表的分布列(distribute column)进行修改。
- UPDATE操作频繁的表不建议创建为复制表。
- 列存表约束:
- 列存表,暂时不支持RETURNING子句。
- 列存表不支持结果不确定的更新(non-deterministic update)。试图对列存表用多行数据更新一行时会报错。
- 列存表的更新操作,旧记录空间不会回收,需要执行VACUUM FULL table_name进行清理。
- 对于列存表,支持轻量化UPDATE操作。轻量化UPDATE只重写更新列,减少空间使用量。列存轻量化UPDATE通过GUC参数enable_light_colupdate控制是否开启,默认关闭。
- 列存轻量化UPDATE在以下场景不能使用:更新索引列,更新主键列,更新分区列,更新PCK列和在线扩容,会自动转化为普通UPDATE方式。
- 列存轻量化UPDATE与后台列存AUTOVACUUM并发会小概率报错,可以通过ALTER TABLE设置表级参数enable_column_autovacuum_garbage为off来避免。需要注意的是设置表级参数enable_column_autovacuum_garbage为off会关闭该表的后台列存AUTOVACUUM。
语法格式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
UPDATE [/*+ plan_hint */] [ ONLY ] table_name [ * ] [ [ AS ] alias ] [ PARTITION ( partition_name ) | PARTITION FOR ( partition_key_value [, ...] ) ] SET {column_name = { expression | DEFAULT } |( column_name [, ...] ) = {( { expression | DEFAULT } [, ...] ) |sub_query }}[, ...] [ FROM from_list] [ WHERE condition ] [ RETURNING {* | {output_expression [ [ AS ] output_name ]} [, ...] }]; where sub_query can be: SELECT [ ALL | DISTINCT [ ON ( expression [, ...] ) ] ] { * | {expression [ [ AS ] output_name ]} [, ...] } [ FROM from_item [, ...] ] [ WHERE condition ] [ GROUP BY grouping_element [, ...] ] [ HAVING condition [, ...] ] |
参数说明
参数 |
描述 |
示例 |
||||
---|---|---|---|---|---|---|
plan_hint子句 |
以/*+ */的形式在关键字后,用于对指定语句块生成的计划进行hint调优,详细用法请参见使用Plan Hint进行调优。 |
- |
||||
table_name |
要更新的表名,可以带上schema名。 |
test.table1,其中test为schema名。 |
||||
[AS] alias |
为表指定一个简短的别名,方便引用,例如alias。字符串,符合标识符命名规范,详情请参见标识符命名规范。 |
更新别名为e的表employees:
|
||||
partition_name |
分区名称,该子句指定要更新的分区名称对应的数据,适用于范围分区、列表分区等已命名的分区表。仅8.2.1及以上集群版本支持该选项。 |
更新分区名为p1的数据:
|
||||
partition_key_value |
分区键值,该子句指定要更新的分区键值对应的数据。 通过PARTITION FOR ( partition_key_value [, ...] )子句指定的这一组值,可以唯一确定一个分区。 |
更新分区键值为41的数据行,将相应的b值改为20:
|
||||
column_name |
要修改的字段名。 支持使用目标表的表名加字段名来引用这个字段。例如:
支持使用目标表的别名加字段名来引用这个字段。例如:
|
- |
||||
expression |
赋给字段的值或表达式。 |
给商品价格打8折:
|
||||
DEFAULT |
用对应字段的缺省值填充该字段。如果没有缺省值,则为NULL。 |
重置非活跃用户的状态和登录次数:
|
||||
sub_query |
子查询。使用同一个数据库里的其他表的查询结果,来更新目标表。 其中SELECT子句具体介绍请参考SELECT。 |
使用子查询更新部门平均工资:
|
||||
from_list |
另一个表的表达式列表,允许在WHERE条件里关联其他表字段来更新目标表。与在一个SELECT语句的FROM子句里声明表列表类似。
须知:
目标表绝对不能出现在from_list里,除非在使用一个自连接(此时它必须以from_list的别名出现)。 |
|||||
condition |
跟在WHERE后面的一个返回boolean类型结果的表达式。只有这个表达式返回true的行才会被更新。 |
只更新数学成绩低于80分的学生:
|
||||
output_expression |
在所有需要更新的行都被更新之后,用于计算返回值的表达式,便于确认更新结果。 取值范围:使用任何table以及FROM中列出的表的字段。*表示返回所有字段。 |
更新并返回修改前后的库存信息:
|
||||
output_name |
给RETURNING返回的列起的别名,将输出结果优化。字符串,符合标识符命名规范,详情请参见标识符命名规范。 |
- |
示例:常用UPDATE场景
创建样例表并插入数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
DROP TABLE IF EXISTS products; CREATE TABLE products ( product_id INT PRIMARY KEY, --商品ID name VARCHAR(100), --商品名称 price DECIMAL(10,2), --商品价格 stock INT, --商品库存 last_updated TIMESTAMP --数据更新时间 ); INSERT INTO products VALUES (101, 'Phone', 2999.00, 50, NULL), (102, 'PC', 5999.00, 30, NULL), (103, 'PAD', 1999.00, 20, NULL); |
不含WHERE子句表示直接更新所有记录的值:所有产品价格上浮20%。
1 2 |
UPDATE products SET price = price * 1.2 , last_updated = NOW(); SELECT * FROM products ORDER BY 1; |
使用WHERE子句更新指定的数据:将平板电脑的价格上涨500元。
1 2 3 |
UPDATE products SET price = price + 500 , last_updated = NOW() WHERE name = 'PAD'; SELECT * FROM products ORDER BY 1; |
使用WHERE子句更新特定范围的数据:商品清库存,需要将库存大于40的商品打8折处理。
1 2 3 |
UPDATE products SET price = price * 0.8 , last_updated = NOW() WHERE stock > 40; SELECT * FROM products ORDER BY 1; |
在一个UPDATE命令中更新多个字段,方法是在SET子句中列出更多赋值:调整PC的价格和库存。
1 2 3 |
UPDATE products SET price = price - 1000 , stock = stock - 10, last_updated = NOW() WHERE name = 'PC'; SELECT * FROM products ORDER BY 1; |
示例:更新分区表的指定分区
创建样例表并导入数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
DROP TABLE IF EXISTS test_range_row; CREATE TABLE test_range_row(a int, b int,d int) DISTRIBUTE BY hash(a) PARTITION BY RANGE(d) ( PARTITION p1 values LESS THAN (60), PARTITION p2 values LESS THAN (75), PARTITION p3 values LESS THAN (90), PARTITION p4 VALUES LESS THAN (maxvalue) ); INSERT INTO test_range_row PARTITION(p1) VALUES(55,20,51); INSERT INTO test_range_row PARTITION(p1) VALUES(54,18,59); INSERT INTO test_range_row PARTITION(p2) VALUES(15,18,63); INSERT INTO test_range_row PARTITION(p3) VALUES(12,21,80); INSERT INTO test_range_row PARTITION(p3) VALUES(83,21,85); INSERT INTO test_range_row PARTITION(p4) VALUES(36,21,97); SELECT * FROM test_range_row ORDER BY d; |
更新分区名称为p1的行数据,将d值修改为41。
1 2 |
UPDATE test_range_row PARTITION(p1) SET d = 41; SELECT * FROM test_range_row ORDER BY d; |
从以上结果看,分区为p1,即d<60的行数据,相应的d值都被修改为41。
继续执行以下SQL,更新分区键值为41的数据行,将相应的b值改为20。
1 2 |
UPDATE test_range_row PARTITION FOR (41) SET b = 20; SELECT * FROM test_range_row ORDER BY d; |
示例:使用FROM from_list子句,优先处理VIP等级高的客户的订单
需要更新目标表:订单表orders,但是需要使用FROM子句关联另一张表customers的客户ID字段,来获取客户等级。
创建客户信息表和订单表,包含的字段如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
DROP TABLE IF EXISTS customers; CREATE TABLE customers ( --创建客户信息表 customer_id INT PRIMARY KEY, --客户ID name VARCHAR(100), --客户姓名 vip_level INT --VIP等级 ); DROP TABLE IF EXISTS orders; CREATE TABLE orders ( --创建订单表 order_id INT PRIMARY KEY, --订单号 customer_id INT , --客户ID amount DECIMAL(10,2), --消费金额 status VARCHAR(20) --订单状态 ); |
插入测试数据,订单状态均为pending,即三个客户的订单都未处理。
1 2 3 4 5 6 7 8 9 |
INSERT INTO customers VALUES (1, '张三', 1), (2, '李四', 3), (3, '王五', 2); INSERT INTO orders VALUES (1001, 1, 500.00, 'pending'), (1002, 2, 1200.00, 'pending'), (1003, 3, 800.00, 'pending'); SELECT * FROM orders; |
由于订单较多,现要求优先处理VIP等级高的客户的订单,此时需要通过FROM from_list子句,关联客户表的客户ID,获取客户VIP等级,本例为,优先处理VIP等级为3级以上的客户订单。
1 2 3 4 5 6 |
UPDATE orders o SET status = 'priority' --将订单状态修改为“高优先级” FROM customers c WHERE o.customer_id = c.customer_id AND c.vip_level >= 3; --从客户表中获取等级VIP为3级以上的客户 SELECT * FROM orders; |
示例:使用DEFAULT重置非活跃用户的状态
创建用户表(带默认值),并插入数据:
1 2 3 4 5 6 7 8 9 10 11 12 |
DROP TABLE IF EXISTS users; CREATE TABLE users ( user_id INT PRIMARY KEY, --用户ID username VARCHAR(50) , --用户名 login_count INT DEFAULT 0, --登录次数,默认为0 last_login TIMESTAMP, --最后一次登录的时间 status VARCHAR(20) DEFAULT 'active'); --用户状态,默认为active INSERT INTO users (user_id, username, login_count, last_login) VALUES (1, 'user1', 5, '2023-06-01 10:00:00'), (2, 'user2', 3, '2023-06-15 14:30:00'); SELECT * FROM users; |
重置非活跃用户的状态和登录次数:
1 2 3 4 5 |
UPDATE users SET status = DEFAULT, -- 重置为'active' login_count = DEFAULT -- 重置为0 WHERE last_login < CURRENT_DATE - INTERVAL '30 days'; SELECT * FROM users; |