数据更新 #

一、UPDATE基础 #

1.1 基本语法 #

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

-- 示例表
CREATE TABLE products (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL,
    price DECIMAL(10, 2),
    stock INT DEFAULT 0,
    status VARCHAR(20) DEFAULT 'active',
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

INSERT INTO products (name, price, stock) VALUES
    ('Product A', 99.99, 100),
    ('Product B', 149.99, 50),
    ('Product C', 199.99, 30);

-- 更新单个字段
UPDATE products 
SET price = 89.99 
WHERE id = 1;

-- 查看结果
SELECT * FROM products WHERE id = 1;

1.2 更新多个字段 #

sql
-- 更新多个字段
UPDATE products 
SET 
    price = 79.99,
    stock = 150,
    status = 'promoted'
WHERE id = 1;

-- 使用表达式更新
UPDATE products 
SET 
    price = price * 0.9,  -- 打9折
    stock = stock + 50    -- 增加库存
WHERE id = 2;

SELECT * FROM products;

1.3 WHERE子句 #

sql
-- 使用各种条件更新

-- 等于条件
UPDATE products SET status = 'inactive' WHERE id = 3;

-- 范围条件
UPDATE products 
SET price = price * 0.8 
WHERE price > 100;

-- IN 条件
UPDATE products 
SET status = 'featured' 
WHERE id IN (1, 2);

-- LIKE 条件
UPDATE products 
SET status = 'sale' 
WHERE name LIKE 'Product%';

-- 多条件组合
UPDATE products 
SET price = price * 0.95 
WHERE stock > 50 AND status = 'active';

二、安全更新 #

2.1 避免全表更新 #

sql
-- 危险操作: 没有 WHERE 子句
UPDATE products SET price = 0;
-- 这会更新所有行!

-- 安全做法: 始终使用 WHERE 子句
UPDATE products 
SET price = 0 
WHERE id = 999;  -- 明确条件

-- 使用 LIMIT 限制影响行数
UPDATE products 
SET status = 'inactive' 
WHERE stock = 0 
LIMIT 100;

2.2 先查询后更新 #

sql
-- 先查询确认要更新的数据
SELECT * FROM products 
WHERE stock = 0 AND status = 'active';

-- 确认无误后再更新
UPDATE products 
SET status = 'out_of_stock' 
WHERE stock = 0 AND status = 'active';

-- 使用事务确保安全
START TRANSACTION;

SELECT * FROM products WHERE id = 1 FOR UPDATE;

UPDATE products SET price = 59.99 WHERE id = 1;

-- 确认结果
SELECT * FROM products WHERE id = 1;

COMMIT;

2.3 使用事务 #

sql
-- 关键更新使用事务
START TRANSACTION;

-- 更新库存
UPDATE products 
SET stock = stock - 1 
WHERE id = 1 AND stock > 0;

-- 检查是否成功
SELECT ROW_COUNT() AS affected_rows;

-- 如果 affected_rows = 0,说明库存不足
-- 可以选择回滚
-- ROLLBACK;

-- 确认后提交
COMMIT;

三、表达式更新 #

3.1 算术运算 #

sql
-- 加减乘除
UPDATE products 
SET 
    price = price + 10,        -- 加价
    stock = stock - 5          -- 减库存
WHERE id = 1;

-- 乘除
UPDATE products 
SET price = price * 1.1        -- 涨价10%
WHERE category = 'electronics';

UPDATE products 
SET price = price / 2          -- 半价
WHERE status = 'clearance';

-- 取模
UPDATE products 
SET stock = stock - (stock % 10)  -- 库存取整
WHERE id = 1;

3.2 字符串函数 #

sql
-- 字符串拼接
UPDATE products 
SET name = CONCAT(name, ' - New')
WHERE id = 1;

-- 大小写转换
UPDATE products 
SET name = UPPER(name)
WHERE id = 1;

UPDATE products 
SET name = LOWER(name)
WHERE id = 2;

-- 字符串替换
UPDATE products 
SET name = REPLACE(name, 'Old', 'New')
WHERE name LIKE '%Old%';

-- 截取字符串
UPDATE products 
SET name = LEFT(name, 50)
WHERE LENGTH(name) > 50;

3.3 日期函数 #

sql
-- 创建带日期的表
CREATE TABLE orders (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT,
    amount DECIMAL(10, 2),
    status VARCHAR(20),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    expired_at TIMESTAMP
);

-- 更新日期
UPDATE orders 
SET expired_at = DATE_ADD(created_at, INTERVAL 30 DAY)
WHERE expired_at IS NULL;

-- 更新状态
UPDATE orders 
SET status = 'expired'
WHERE expired_at < NOW() AND status = 'pending';

-- 设置特定日期
UPDATE orders 
SET created_at = '2024-01-01 00:00:00'
WHERE id = 1;

3.4 CASE表达式 #

sql
-- 使用 CASE 条件更新
UPDATE products 
SET status = CASE
    WHEN stock = 0 THEN 'out_of_stock'
    WHEN stock < 10 THEN 'low_stock'
    WHEN stock < 50 THEN 'normal'
    ELSE 'sufficient'
END;

-- 多字段 CASE 更新
UPDATE products 
SET 
    status = CASE
        WHEN stock = 0 THEN 'out_of_stock'
        ELSE status
    END,
    price = CASE
        WHEN stock > 100 THEN price * 0.9
        ELSE price
    END;

四、多表更新 #

4.1 关联更新 #

sql
-- 创建关联表
CREATE TABLE categories (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    discount DECIMAL(5, 2)
);

INSERT INTO categories (name, discount) VALUES
    ('Electronics', 10),
    ('Clothing', 20),
    ('Books', 15);

ALTER TABLE products ADD COLUMN category_id BIGINT;
UPDATE products SET category_id = 1 WHERE id IN (1, 2);
UPDATE products SET category_id = 2 WHERE id = 3;

-- 关联更新
UPDATE products p
JOIN categories c ON p.category_id = c.id
SET p.price = p.price * (1 - c.discount / 100)
WHERE c.discount > 0;

SELECT p.id, p.name, p.price, c.name AS category, c.discount
FROM products p
JOIN categories c ON p.category_id = c.id;

4.2 子查询更新 #

sql
-- 使用子查询更新
UPDATE products 
SET price = (
    SELECT AVG(price) FROM products WHERE category_id = 1
)
WHERE id = 1;

-- 使用子查询条件
UPDATE products 
SET status = 'premium'
WHERE category_id IN (
    SELECT id FROM categories WHERE discount > 15
);

-- 使用 EXISTS
UPDATE products p
SET p.status = 'discounted'
WHERE EXISTS (
    SELECT 1 FROM categories c 
    WHERE c.id = p.category_id AND c.discount > 10
);

五、批量更新 #

5.1 条件批量更新 #

sql
-- 批量更新满足条件的记录
UPDATE products 
SET status = 'inactive'
WHERE stock = 0;

-- 分批更新大量数据
-- 每次更新 1000 条
UPDATE products 
SET status = 'processed'
WHERE status = 'pending'
LIMIT 1000;

-- 循环执行直到影响行数为 0

5.2 使用CASE批量更新 #

sql
-- 批量更新不同值
UPDATE products 
SET price = CASE id
    WHEN 1 THEN 89.99
    WHEN 2 THEN 139.99
    WHEN 3 THEN 189.99
    ELSE price
END
WHERE id IN (1, 2, 3);

-- 批量更新多个字段
UPDATE products 
SET 
    price = CASE id
        WHEN 1 THEN 79.99
        WHEN 2 THEN 129.99
        ELSE price
    END,
    stock = CASE id
        WHEN 1 THEN 200
        WHEN 2 THEN 100
        ELSE stock
    END
WHERE id IN (1, 2);

5.3 临时表批量更新 #

sql
-- 创建临时表存储更新数据
CREATE TEMPORARY TABLE temp_updates (
    product_id BIGINT,
    new_price DECIMAL(10, 2),
    new_stock INT
);

INSERT INTO temp_updates VALUES
    (1, 69.99, 300),
    (2, 119.99, 200),
    (3, 169.99, 150);

-- 关联临时表更新
UPDATE products p
JOIN temp_updates t ON p.id = t.product_id
SET 
    p.price = t.new_price,
    p.stock = t.new_stock;

-- 删除临时表
DROP TEMPORARY TABLE temp_updates;

SELECT * FROM products;

六、ORDER BY和LIMIT #

6.1 按顺序更新 #

sql
-- 按价格排序,更新前3个产品
UPDATE products 
SET status = 'top_seller'
ORDER BY price DESC
LIMIT 3;

-- 按库存排序,更新库存最少的
UPDATE products 
SET status = 'need_restock'
ORDER BY stock ASC
LIMIT 5;

SELECT * FROM products ORDER BY stock ASC;

6.2 分页更新 #

sql
-- 分页更新大量数据
-- 第1页
UPDATE products 
SET status = 'processed'
WHERE status = 'pending'
ORDER BY id
LIMIT 1000;

-- 第2页 (需要记录上一页最后ID)
UPDATE products 
SET status = 'processed'
WHERE status = 'pending' AND id > 1000
ORDER BY id
LIMIT 1000;

七、更新性能优化 #

7.1 性能建议 #

text
UPDATE 性能优化建议
┌─────────────────────────────────────────────────────────────┐
│                                                             │
│   1. 使用索引条件                                           │
│   ├── WHERE 条件使用索引列                                 │
│   └── 避免全表扫描                                         │
│                                                             │
│   2. 分批更新                                               │
│   ├── 大量更新分批进行                                     │
│   └── 使用 LIMIT 控制批次大小                              │
│                                                             │
│   3. 避免长事务                                             │
│   ├── 长时间持有锁影响并发                                 │
│   └── 及时提交事务                                         │
│                                                             │
│   4. 减少锁范围                                             │
│   ├── 精确的 WHERE 条件                                    │
│   └── 避免锁定不必要的数据                                 │
│                                                             │
│   5. 非高峰期执行                                           │
│   ├── 大批量更新在低峰期执行                               │
│   └── 减少对业务的影响                                     │
│                                                             │
└─────────────────────────────────────────────────────────────┘

7.2 查看执行计划 #

sql
-- 查看更新执行计划
EXPLAIN UPDATE products 
SET price = 99.99 
WHERE id = 1;

-- 分析执行计划
EXPLAIN ANALYZE UPDATE products 
SET price = 99.99 
WHERE id = 1;

八、常见问题 #

8.1 更新冲突 #

sql
-- 问题: 并发更新冲突
-- 会话1
START TRANSACTION;
UPDATE products SET price = 50 WHERE id = 1;
-- 不提交

-- 会话2 (会等待锁)
UPDATE products SET price = 60 WHERE id = 1;
-- 等待会话1释放锁

-- 解决方案: 使用乐观锁
UPDATE products 
SET price = 60, version = version + 1 
WHERE id = 1 AND version = 1;

-- 检查影响行数
SELECT ROW_COUNT();
-- 如果为0,说明版本已变化,需要重试

8.2 更新超时 #

sql
-- 问题: 更新超时
-- Error: Lock wait timeout exceeded

-- 解决方案1: 增加超时时间
SET SESSION innodb_lock_wait_timeout = 50;

-- 解决方案2: 使用 NOWAIT
UPDATE products 
SET price = 99.99 
WHERE id = 1 
FOR UPDATE NOWAIT;

-- 解决方案3: 检查并终止长时间运行的事务
SELECT * FROM INFORMATION_SCHEMA.PROCESSLIST 
WHERE TIME > 10 AND STATE LIKE '%lock%';

8.3 更新导致热点 #

sql
-- 问题: 频繁更新同一行导致热点
UPDATE counters SET value = value + 1 WHERE id = 1;

-- 解决方案: 分散更新
-- 使用多个计数器
UPDATE counters SET value = value + 1 
WHERE id = (RAND() * 10 + 1);

-- 汇总时求和
SELECT SUM(value) FROM counters;

九、总结 #

UPDATE 语句要点:

类型 说明
基本更新 单字段、多字段、WHERE条件
表达式更新 算术、字符串、日期、CASE
多表更新 JOIN、子查询
批量更新 CASE批量、临时表
安全更新 事务、LIMIT、先查后改
性能优化 索引、分批、避免长事务

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

最后更新:2026-03-27