MariaDB数据更新 #

一、UPDATE语句概述 #

1.1 基本语法 #

sql
UPDATE table_name
SET column1 = value1, column2 = value2, ...
WHERE condition;

1.2 示例表结构 #

sql
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    email VARCHAR(100),
    age INT DEFAULT 0,
    status TINYINT DEFAULT 1,
    balance DECIMAL(10,2) DEFAULT 0.00,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

INSERT INTO users (name, email, age, status, balance) VALUES
    ('John', 'john@example.com', 25, 1, 1000.00),
    ('Jane', 'jane@example.com', 30, 1, 2000.00),
    ('Bob', 'bob@example.com', 28, 0, 500.00),
    ('Alice', 'alice@example.com', 22, 1, 1500.00);

二、基本更新 #

2.1 更新单个字段 #

sql
-- 更新单个字段
UPDATE users SET age = 26 WHERE id = 1;

-- 更新后查看
SELECT * FROM users WHERE id = 1;
+----+------+-------------------+------+--------+---------+---------------------+
| id | name | email             | age  | status | balance | updated_at          |
+----+------+-------------------+------+--------+---------+---------------------+
|  1 | John | john@example.com  |   26 |      1 | 1000.00 | 2024-01-15 10:30:00 |
+----+------+-------------------+------+--------+---------+---------------------+

2.2 更新多个字段 #

sql
-- 更新多个字段
UPDATE users 
SET age = 27, 
    email = 'john.new@example.com'
WHERE id = 1;

-- 使用表达式
UPDATE users 
SET balance = balance + 100,
    updated_at = NOW()
WHERE id = 1;

2.3 条件更新 #

sql
-- 使用WHERE条件
UPDATE users SET status = 0 WHERE age > 30;

-- 使用多个条件
UPDATE users 
SET status = 1 
WHERE age >= 18 AND age <= 60;

-- 使用IN条件
UPDATE users 
SET status = 0 
WHERE id IN (1, 2, 3);

-- 使用BETWEEN
UPDATE users 
SET balance = balance * 1.1 
WHERE age BETWEEN 25 AND 35;

-- 使用LIKE
UPDATE users 
SET status = 0 
WHERE email LIKE '%@test.com';

三、安全更新 #

3.1 避免全表更新 #

sql
-- 危险:没有WHERE条件会更新所有行
UPDATE users SET status = 0;  -- 更新所有行!

-- 安全做法:始终使用WHERE条件
UPDATE users SET status = 0 WHERE id = 1;

-- 启用安全更新模式
SET sql_safe_updates = 1;

-- 安全模式下,必须使用KEY或LIMIT
UPDATE users SET status = 0 WHERE id = 1;  -- OK
UPDATE users SET status = 0 WHERE name = 'John';  -- 错误
UPDATE users SET status = 0 LIMIT 1;  -- OK

3.2 使用LIMIT #

sql
-- 限制更新的行数
UPDATE users 
SET status = 0 
WHERE status = 1 
LIMIT 10;

-- 分批更新(避免锁表时间过长)
-- 每次更新1000行
UPDATE users 
SET status = 0 
WHERE status = 1 
LIMIT 1000;

3.3 使用ORDER BY #

sql
-- 按顺序更新
UPDATE users 
SET status = 0 
ORDER BY created_at DESC 
LIMIT 10;

-- 更新最旧的记录
UPDATE users 
SET status = 1 
ORDER BY created_at ASC 
LIMIT 5;

四、表达式更新 #

4.1 算术表达式 #

sql
-- 增加余额
UPDATE users SET balance = balance + 100 WHERE id = 1;

-- 减少余额
UPDATE users SET balance = balance - 50 WHERE id = 1;

-- 乘法
UPDATE users SET balance = balance * 1.1 WHERE status = 1;

-- 除法
UPDATE users SET balance = balance / 2 WHERE status = 0;

4.2 字符串表达式 #

sql
-- 字符串连接
UPDATE users 
SET email = CONCAT(name, '@company.com')
WHERE email IS NULL;

-- 字符串替换
UPDATE users 
SET email = REPLACE(email, '@old.com', '@new.com');

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

4.3 条件表达式 #

sql
-- 使用CASE WHEN
UPDATE users 
SET status = CASE 
    WHEN age < 18 THEN 0
    WHEN age >= 18 AND age <= 60 THEN 1
    ELSE 0
END;

-- 使用IF函数
UPDATE users 
SET status = IF(age >= 18, 1, 0);

-- 使用NULLIF
UPDATE users 
SET email = NULLIF(email, '');

4.4 日期表达式 #

sql
-- 更新日期
UPDATE users 
SET updated_at = NOW()
WHERE id = 1;

-- 日期计算
UPDATE users 
SET expiry_date = DATE_ADD(expiry_date, INTERVAL 30 DAY)
WHERE status = 1;

-- 使用当前日期
UPDATE users 
SET last_login = CURRENT_TIMESTAMP
WHERE id = 1;

五、多表更新 #

5.1 UPDATE JOIN #

sql
-- 创建订单表
CREATE TABLE orders (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT,
    amount DECIMAL(10,2),
    status VARCHAR(20)
);

-- 使用JOIN更新
UPDATE users u
JOIN orders o ON u.id = o.user_id
SET u.balance = u.balance + o.amount
WHERE o.status = 'completed';

-- 使用LEFT JOIN
UPDATE users u
LEFT JOIN orders o ON u.id = o.user_id
SET u.status = 0
WHERE o.id IS NULL;

5.2 多表关联更新 #

sql
-- 更新多个表
UPDATE users u
JOIN orders o ON u.id = o.user_id
JOIN products p ON o.product_id = p.id
SET u.balance = u.balance - o.amount,
    o.status = 'paid'
WHERE o.id = 1;

-- 使用子查询更新
UPDATE users 
SET balance = (
    SELECT SUM(amount) 
    FROM orders 
    WHERE user_id = users.id AND status = 'completed'
)
WHERE id = 1;

六、子查询更新 #

6.1 使用子查询 #

sql
-- 使用子查询作为条件
UPDATE users 
SET status = 0 
WHERE id IN (
    SELECT user_id FROM orders WHERE status = 'cancelled'
);

-- 使用子查询计算值
UPDATE users 
SET balance = (
    SELECT COALESCE(SUM(amount), 0) 
    FROM orders 
    WHERE user_id = users.id
);

-- 使用EXISTS
UPDATE users 
SET status = 0 
WHERE EXISTS (
    SELECT 1 FROM orders 
    WHERE user_id = users.id 
    AND status = 'cancelled'
);

6.2 关联子查询 #

sql
-- 更新每个用户的订单数
UPDATE users u
SET order_count = (
    SELECT COUNT(*) 
    FROM orders o 
    WHERE o.user_id = u.id
);

-- 更新用户的最新订单日期
UPDATE users u
SET last_order_date = (
    SELECT MAX(created_at) 
    FROM orders o 
    WHERE o.user_id = u.id
);

七、JSON字段更新 #

7.1 更新JSON字段 #

sql
-- 创建包含JSON的表
CREATE TABLE products (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100),
    attributes JSON
);

INSERT INTO products (name, attributes) VALUES
    ('Laptop', '{"brand": "Dell", "ram": "16GB", "price": 1000}');

-- 更新整个JSON
UPDATE products 
SET attributes = '{"brand": "HP", "ram": "32GB", "price": 1200}'
WHERE id = 1;

-- 使用JSON函数更新
UPDATE products 
SET attributes = JSON_SET(attributes, '$.price', 1100)
WHERE id = 1;

-- 添加新属性
UPDATE products 
SET attributes = JSON_SET(attributes, '$.color', 'black')
WHERE id = 1;

-- 删除属性
UPDATE products 
SET attributes = JSON_REMOVE(attributes, '$.ram')
WHERE id = 1;

7.2 JSON数组更新 #

sql
-- 更新数组元素
UPDATE products 
SET attributes = JSON_SET(attributes, '$.tags[0]', 'new_tag')
WHERE id = 1;

-- 追加数组元素
UPDATE products 
SET attributes = JSON_ARRAY_APPEND(attributes, '$.tags', 'tag3')
WHERE id = 1;

-- 插入数组元素
UPDATE products 
SET attributes = JSON_ARRAY_INSERT(attributes, '$.tags[0]', 'first_tag')
WHERE id = 1;

八、MariaDB特有功能 #

8.1 RETURNING子句 #

sql
-- MariaDB 10.5+ 支持RETURNING
UPDATE users 
SET balance = balance + 100 
WHERE id = 1
RETURNING id, name, balance;

+----+------+---------+
| id | name | balance |
+----+------+---------+
|  1 | John | 1100.00 |
+----+------+---------+

8.2 延迟更新 #

sql
-- 低优先级更新
UPDATE LOW_PRIORITY users 
SET status = 0 
WHERE id = 1;

-- 等待所有读取完成后执行

8.3 忽略错误 #

sql
-- 忽略错误继续更新
UPDATE IGNORE users 
SET email = 'duplicate@example.com'
WHERE id = 1;

-- 查看警告
SHOW WARNINGS;

九、事务中的更新 #

9.1 使用事务 #

sql
-- 开始事务
START TRANSACTION;

-- 执行更新
UPDATE users SET balance = balance - 100 WHERE id = 1;
UPDATE users SET balance = balance + 100 WHERE id = 2;

-- 提交事务
COMMIT;

-- 或回滚
ROLLBACK;

9.2 乐观锁 #

sql
-- 使用版本号实现乐观锁
UPDATE users 
SET balance = balance + 100,
    version = version + 1
WHERE id = 1 AND version = 5;

-- 检查是否更新成功
SELECT ROW_COUNT();
-- 返回1表示成功,0表示失败(版本不匹配)

十、性能优化 #

10.1 索引优化 #

sql
-- 确保WHERE条件使用索引
-- 创建索引
CREATE INDEX idx_users_status ON users(status);

-- 使用索引的更新
UPDATE users SET balance = 0 WHERE status = 1;

-- 查看执行计划
EXPLAIN UPDATE users SET balance = 0 WHERE status = 1;

10.2 分批更新 #

sql
-- 大表分批更新
-- 方法1:使用LIMIT循环
-- 每次更新1000行
UPDATE users 
SET status = 0 
WHERE status = 1 
LIMIT 1000;

-- 方法2:使用ID范围
UPDATE users 
SET status = 0 
WHERE id BETWEEN 1 AND 1000 AND status = 1;

-- 方法3:使用存储过程
DELIMITER //
CREATE PROCEDURE batch_update()
BEGIN
    DECLARE done INT DEFAULT 0;
    WHILE done = 0 DO
        UPDATE users SET status = 0 WHERE status = 1 LIMIT 1000;
        IF ROW_COUNT() = 0 THEN
            SET done = 1;
        END IF;
        DO SLEEP(0.1);  -- 短暂休息
    END WHILE;
END //
DELIMITER ;

10.3 避免全表扫描 #

sql
-- 避免:没有索引的条件
UPDATE users SET status = 0 WHERE YEAR(created_at) = 2024;

-- 优化:使用范围条件
UPDATE users SET status = 0 
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';

十一、常见问题 #

11.1 更新不生效 #

sql
-- 检查WHERE条件
SELECT * FROM users WHERE id = 999;  -- 确认数据存在

-- 检查权限
SHOW GRANTS FOR CURRENT_USER();

-- 检查事务
SELECT @@autocommit;  -- 是否自动提交

11.2 更新被阻塞 #

sql
-- 查看锁等待
SHOW PROCESSLIST;

-- 查看锁信息
SELECT * FROM information_schema.innodb_locks;
SELECT * FROM information_schema.innodb_lock_waits;

-- 终止阻塞的进程
KILL <process_id>;

11.3 更新超时 #

sql
-- 设置锁等待超时
SET innodb_lock_wait_timeout = 50;  -- 默认50秒

-- 查看当前设置
SHOW VARIABLES LIKE 'innodb_lock_wait_timeout';

十二、更新日志 #

12.1 记录更新历史 #

sql
-- 创建更新日志表
CREATE TABLE user_update_log (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT,
    old_balance DECIMAL(10,2),
    new_balance DECIMAL(10,2),
    updated_by VARCHAR(50),
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- 使用触发器记录更新
DELIMITER //
CREATE TRIGGER log_user_update
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
    IF OLD.balance != NEW.balance THEN
        INSERT INTO user_update_log (user_id, old_balance, new_balance, updated_by)
        VALUES (NEW.id, OLD.balance, NEW.balance, CURRENT_USER());
    END IF;
END //
DELIMITER ;

十三、总结 #

更新数据要点:

操作 语法 说明
单表更新 UPDATE … SET … WHERE 基本更新
多表更新 UPDATE … JOIN … SET 关联更新
条件更新 CASE WHEN / IF 条件表达式
JSON更新 JSON_SET / JSON_REMOVE JSON字段更新
返回结果 RETURNING MariaDB 10.5+

最佳实践:

  1. 始终使用WHERE条件
  2. 使用事务保证数据一致性
  3. 大批量更新分批执行
  4. 使用索引优化更新性能
  5. 记录重要数据的更新历史

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

最后更新:2026-03-27