数据更新 #

一、UPDATE基础 #

1.1 基本语法 #

sql
-- 基本更新语法
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;

-- 示例表
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100),
    age INT,
    status VARCHAR(20) DEFAULT 'active',
    updated_at TIMESTAMP DEFAULT NOW()
);

INSERT INTO users (name, email, age) VALUES
    ('Alice', 'alice@example.com', 25),
    ('Bob', 'bob@example.com', 30),
    ('Carol', 'carol@example.com', 28);

-- 更新单行
UPDATE users
SET age = 26
WHERE name = 'Alice';

-- 查看结果
SELECT * FROM users WHERE name = 'Alice';

1.2 更新多个列 #

sql
-- 更新多个列
UPDATE users
SET 
    email = 'alice.new@example.com',
    age = 27,
    status = 'verified'
WHERE name = 'Alice';

-- 使用表达式更新
UPDATE users
SET 
    age = age + 1,
    updated_at = NOW()
WHERE name = 'Alice';

1.3 更新所有行 #

sql
-- 更新所有行 (慎用!)
UPDATE users
SET status = 'inactive';

-- 通常应该加 WHERE 条件
UPDATE users
SET status = 'inactive'
WHERE age > 50;

二、条件更新 #

2.1 使用WHERE条件 #

sql
-- 等值条件
UPDATE users
SET status = 'verified'
WHERE email = 'alice@example.com';

-- 范围条件
UPDATE users
SET status = 'senior'
WHERE age >= 30;

-- 多条件组合
UPDATE users
SET status = 'premium'
WHERE age > 25 AND email LIKE '%@company.com';

-- IN 条件
UPDATE users
SET status = 'vip'
WHERE name IN ('Alice', 'Bob', 'Carol');

-- 子查询条件
UPDATE users
SET status = 'active_buyer'
WHERE id IN (
    SELECT DISTINCT user_id 
    FROM orders 
    WHERE created_at > NOW() - INTERVAL '30 days'
);

2.2 使用CASE表达式 #

sql
-- 使用 CASE 条件更新
UPDATE users
SET status = CASE
    WHEN age < 25 THEN 'junior'
    WHEN age >= 25 AND age < 35 THEN 'mid-level'
    WHEN age >= 35 THEN 'senior'
    ELSE 'unknown'
END;

-- 带条件的 CASE 更新
UPDATE users
SET 
    status = CASE
        WHEN age >= 30 THEN 'senior'
        ELSE status  -- 保持原值
    END,
    updated_at = NOW()
WHERE email IS NOT NULL;

三、批量更新 #

3.1 批量更新策略 #

text
批量更新策略
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   1. 分批更新                                               │
│   ├── 每次更新一定数量                                     │
│   ├── 减少锁持有时间                                       │
│   └── 降低冲突概率                                         │
│                                                             │
│   2. 使用事务                                               │
│   ├── 保证原子性                                           │
│   └── 可回滚                                               │
│                                                             │
│   3. 避免热点                                               │
│   ├── 分散更新                                             │
│   └── 使用队列                                             │
│                                                             │
│   4. 使用 CTE                                               │
│   ├── 复杂更新逻辑                                         │
│   └── 提高可读性                                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

3.2 分批更新示例 #

sql
-- 分批更新大量数据
-- 每次更新 1000 行

-- 方法1: 使用 LIMIT
UPDATE users
SET status = 'processed'
WHERE status = 'pending'
LIMIT 1000;

-- 方法2: 使用 ID 范围
UPDATE users
SET status = 'processed'
WHERE id >= 'uuid-start' AND id < 'uuid-end'
  AND status = 'pending';

-- 方法3: 使用事务
BEGIN;
UPDATE users
SET status = 'processed'
WHERE status = 'pending'
LIMIT 1000;
COMMIT;

3.3 使用CTE更新 #

sql
-- 使用 CTE 进行复杂更新
WITH active_users AS (
    SELECT user_id, COUNT(*) as order_count
    FROM orders
    WHERE created_at > NOW() - INTERVAL '30 days'
    GROUP BY user_id
    HAVING COUNT(*) > 5
)
UPDATE users u
SET status = 'active_buyer'
FROM active_users a
WHERE u.id = a.user_id;

-- 使用 CTE 更新并返回结果
WITH updated AS (
    UPDATE users
    SET status = 'verified'
    WHERE email LIKE '%@company.com'
    RETURNING *
)
SELECT * FROM updated;

四、多表更新 #

4.1 使用FROM子句 #

sql
-- 从其他表获取数据更新
UPDATE users u
SET 
    total_orders = o.order_count,
    total_amount = o.total_amount
FROM (
    SELECT 
        user_id,
        COUNT(*) as order_count,
        SUM(amount) as total_amount
    FROM orders
    GROUP BY user_id
) o
WHERE u.id = o.user_id;

-- 使用 JOIN 更新
UPDATE users u
SET status = 'premium'
FROM orders o
WHERE u.id = o.user_id
  AND o.amount > 10000
  AND o.created_at > NOW() - INTERVAL '1 year';

4.2 使用子查询 #

sql
-- 使用子查询更新
UPDATE users
SET status = 'vip'
WHERE id IN (
    SELECT user_id
    FROM orders
    WHERE amount > 10000
    GROUP BY user_id
    HAVING SUM(amount) > 50000
);

-- 使用相关子查询
UPDATE users u
SET last_order_date = (
    SELECT MAX(created_at)
    FROM orders o
    WHERE o.user_id = u.id
);

五、RETURNING子句 #

5.1 返回更新结果 #

sql
-- 返回更新的行
UPDATE users
SET age = age + 1
WHERE name = 'Alice'
RETURNING *;

-- 返回特定列
UPDATE users
SET status = 'verified'
WHERE email = 'alice@example.com'
RETURNING id, name, status;

-- 返回计算结果
UPDATE users
SET 
    age = age + 1,
    updated_at = NOW()
WHERE name = 'Alice'
RETURNING 
    id,
    name,
    age,
    age - 1 AS previous_age,
    updated_at;

5.2 使用返回结果 #

sql
-- 将更新结果插入到另一个表
WITH updated AS (
    UPDATE users
    SET status = 'verified'
    WHERE email LIKE '%@company.com'
    RETURNING id, name, status
)
INSERT INTO user_audit (user_id, action, old_status, new_status)
SELECT id, 'status_update', 'pending', status
FROM updated;

-- 更新并统计
WITH updated AS (
    UPDATE users
    SET status = 'processed'
    WHERE status = 'pending'
    RETURNING *
)
SELECT 
    COUNT(*) as updated_count,
    NOW() as update_time
FROM updated;

六、高级更新 #

6.1 使用JSON更新 #

sql
-- 创建带JSON列的表
CREATE TABLE products (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(100),
    attributes JSONB
);

INSERT INTO products (name, attributes) VALUES
    ('Product A', '{"color": "red", "size": "M", "price": 100}');

-- 更新JSON字段
UPDATE products
SET attributes = jsonb_set(attributes, '{price}', '150')
WHERE name = 'Product A';

-- 添加JSON字段
UPDATE products
SET attributes = attributes || '{"brand": "BrandX"}'
WHERE name = 'Product A';

-- 删除JSON字段
UPDATE products
SET attributes = attributes - 'brand'
WHERE name = 'Product A';

-- 更新嵌套JSON
UPDATE products
SET attributes = jsonb_set(
    attributes,
    '{specs,weight}',
    '500'
)
WHERE name = 'Product A';

6.2 使用数组更新 #

sql
-- 创建带数组列的表
CREATE TABLE tags (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(100),
    tags STRING[]
);

INSERT INTO tags (name, tags) VALUES
    ('Item A', ARRAY['tag1', 'tag2']);

-- 添加数组元素
UPDATE tags
SET tags = array_append(tags, 'tag3')
WHERE name = 'Item A';

-- 删除数组元素
UPDATE tags
SET tags = array_remove(tags, 'tag1')
WHERE name = 'Item A';

-- 替换数组
UPDATE tags
SET tags = ARRAY['new1', 'new2', 'new3']
WHERE name = 'Item A';

七、更新性能 #

7.1 性能建议 #

text
更新性能优化建议
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   1. 使用索引                                               │
│   ├── WHERE 条件使用索引列                                 │
│   └── 加快定位速度                                         │
│                                                             │
│   2. 批量更新                                               │
│   ├── 减少事务次数                                         │
│   └── 提高吞吐量                                           │
│                                                             │
│   3. 避免热点                                               │
│   ├── 分散更新                                             │
│   └── 避免频繁更新同一行                                   │
│                                                             │
│   4. 合理使用事务                                           │
│   ├── 短事务                                               │
│   └── 减少锁持有时间                                       │
│                                                             │
│   5. 使用 RETURNING                                         │
│   ├── 减少额外查询                                         │
│   └── 提高效率                                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

7.2 查看执行计划 #

sql
-- 查看更新执行计划
EXPLAIN UPDATE users
SET status = 'verified'
WHERE email = 'alice@example.com';

-- 查看详细执行计划
EXPLAIN ANALYZE UPDATE users
SET status = 'verified'
WHERE email = 'alice@example.com';

八、常见问题 #

8.1 更新冲突 #

sql
-- 问题: 并发更新冲突
-- 解决: 使用事务和重试

DO $$
BEGIN
    DECLARE
        retry_count INT DEFAULT 0;
    BEGIN
        WHILE retry_count < 3 LOOP
            BEGIN
                UPDATE accounts
                SET balance = balance - 100
                WHERE id = 1;
                COMMIT;
                EXIT;
            EXCEPTION WHEN serialization_failure THEN
                retry_count := retry_count + 1;
                ROLLBACK;
            END;
        END LOOP;
    END;
END;
$$;

8.2 更新不存在的行 #

sql
-- 问题: 更新不存在的行,没有报错也没有更新
UPDATE users SET status = 'verified' WHERE id = 'non-existent-uuid';
-- Query OK, 0 rows affected

-- 解决: 检查更新行数
UPDATE users
SET status = 'verified'
WHERE id = 'non-existent-uuid'
RETURNING id;

-- 或使用 RETURNING 检查
DO $$
DECLARE
    updated_id UUID;
BEGIN
    UPDATE users
    SET status = 'verified'
    WHERE id = 'non-existent-uuid'
    RETURNING id INTO updated_id;
    
    IF updated_id IS NULL THEN
        RAISE EXCEPTION 'User not found';
    END IF;
END;
$$;

8.3 大批量更新 #

sql
-- 问题: 大批量更新导致性能问题
-- 解决: 分批更新

-- 方法1: 分批处理
DO $$
DECLARE
    batch_size INT := 1000;
    total_updated INT := 0;
    batch_updated INT;
BEGIN
    LOOP
        UPDATE users
        SET status = 'processed'
        WHERE status = 'pending'
        LIMIT batch_size;
        
        GET DIAGNOSTICS batch_updated = ROW_COUNT;
        total_updated := total_updated + batch_updated;
        
        EXIT WHEN batch_updated = 0;
        
        COMMIT;
    END LOOP;
    
    RAISE NOTICE 'Total updated: %', total_updated;
END;
$$;

九、总结 #

UPDATE 语句要点:

类型 说明
基本更新 单列、多列、条件更新
批量更新 分批、事务、CTE
多表更新 FROM子句、子查询
RETURNING 返回更新结果
JSON更新 JSONB操作

下一步,让我们学习数据删除操作!

最后更新:2026-03-27