更新时间:2025-08-04 GMT+08:00

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 [, ...] ]

参数说明

表1 UPDATE参数说明

参数

描述

示例

plan_hint子句

以/*+ */的形式在关键字后,用于对指定语句块生成的计划进行hint调优,详细用法请参见使用Plan Hint进行调优

-

table_name

要更新的表名,可以带上schema名。

test.table1,其中test为schema名。

[AS] alias

为表指定一个简短的别名,方便引用,例如alias。字符串,符合标识符命名规范,详情请参见标识符命名规范

更新别名为e的表employees:

1
UPDATE employees AS e SET e.salary = e.salary * 1.1 WHERE e.department = 'technology';

partition_name

分区名称,该子句指定要更新的分区名称对应的数据,适用于范围分区、列表分区等已命名的分区表。仅8.2.1及以上集群版本支持该选项。

更新分区名为p1的数据:

1
UPDATE test_range_row PARTITION(p1) SET d = 41;

partition_key_value

分区键值,该子句指定要更新的分区键值对应的数据。

通过PARTITION FOR ( partition_key_value [, ...] )子句指定的这一组值,可以唯一确定一个分区。

更新分区键值为41的数据行,将相应的b值改为20:

1
UPDATE test_range_row PARTITION FOR (41) SET b = 20;

column_name

要修改的字段名。

支持使用目标表的表名加字段名来引用这个字段。例如:

1
UPDATE foo SET foo.col_name = 'GaussDB';

支持使用目标表的别名加字段名来引用这个字段。例如:

1
UPDATE foo AS f SET f.col_name = 'GaussDB';

-

expression

赋给字段的值或表达式。

给商品价格打8折:

1
UPDATE products SET     price = price * 0.8 WHERE product_id = 101;

DEFAULT

用对应字段的缺省值填充该字段。如果没有缺省值,则为NULL。

重置非活跃用户的状态和登录次数:

1
UPDATE users SET     status = DEFAULT,         login_count = DEFAULT  WHERE last_login < CURRENT_DATE - INTERVAL '30 days';

sub_query

子查询。使用同一个数据库里的其他表的查询结果,来更新目标表。

其中SELECT子句具体介绍请参考SELECT

使用子查询更新部门平均工资:

1
UPDATE departments d SET avg_salary = ( SELECT AVG(salary) FROM employees e WHERE e.dept_id = d.dept_id);

from_list

另一个表的表达式列表,允许在WHERE条件里关联其他表字段来更新目标表。与在一个SELECT语句的FROM子句里声明表列表类似。

须知:

目标表绝对不能出现在from_list里,除非在使用一个自连接(此时它必须以from_list的别名出现)。

示例:使用FROM from_list子句,优先处理VIP等级高的客户的订单

condition

跟在WHERE后面的一个返回boolean类型结果的表达式。只有这个表达式返回true的行才会被更新。

只更新数学成绩低于80分的学生:

1
UPDATE student_scores SET math_score = math_score + 5,    update_time = NOW() WHERE math_score < 80;

output_expression

在所有需要更新的行都被更新之后,用于计算返回值的表达式,便于确认更新结果。

取值范围:使用任何table以及FROM中列出的表的字段。*表示返回所有字段。

更新并返回修改前后的库存信息:

1
UPDATE inventory SET stock = stock - 10 WHERE category = '电子产品' RETURNING   item_id AS "商品ID",    name AS "商品名称",    stock AS "新库存量",    stock + 10 AS "原库存量";

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;