在 Node.js 中使用 mysql2 库批量更新(Bulk Update)多条数据,主要有三种主流方案。由于 MySQL 本身没有像 INSERT 那样简单的 UPDATE ... VALUES (...) 语法,我们需要根据场景选择最合适的方法。
方法一:使用 CASE WHEN 语句(推荐:单条 SQL 完成)
这是最通用的批量更新方法。通过 SQL 的 CASE 语法,可以根据主键 ID 一次性更新多条记录的不同字段。
适用场景: 更新条数在几十到几百条左右,性能较好。
javascript
const mysql = require('mysql2/promise');
async function batchUpdate(data) {
const connection = await mysql.createConnection({/* config */});
// 假设 data 结构为: [{id: 1, name: 'A', age: 20}, {id: 2, name: 'B', age: 25}]
let ids = [];
let nameCases = '';
let ageCases = '';
let params = [];
data.forEach(item => {
ids.push(item.id);
nameCases += `WHEN ? THEN ? `;
params.push(item.id, item.name);
ageCases += `WHEN ? THEN ? `;
params.push(item.id, item.age);
});
// 最后的 params 顺序需要和 SQL 中的问号顺序一致
// 这里为了简化演示直接拼接,实际建议通过数组 push 控制顺序
const sql = `
UPDATE users
SET
name = CASE id ${nameCases} END,
age = CASE id ${ageCases} END
WHERE id IN (${ids.map(() => '?').join(',')})
`;
// 合并参数:[name的id和值..., age的id和值..., WHERE用的id列表]
const finalParams = [...params, ...ids];
await connection.execute(sql, finalParams);
}
方法二:使用 INSERT ... ON DUPLICATE KEY UPDATE (性能最高)
如果你的表有主键(Primary Key)或唯一索引(Unique Index),这是最高效的方法。它的原理是:尝试插入数据,如果主键冲突,则执行更新。
注意: 如果数据不存在,它会变成插入。如果你只想更新不想插入,需要确保传入的 ID 在数据库中已存在。
javascript
const mysql = require('mysql2/promise');
async function batchUpdateUpsert(data) {
const connection = await mysql.createConnection({/* config */});
// 将数据转为二维数组: [[1, 'A', 20], [2, 'B', 25]]
const values = data.map(item => [item.id, item.name, item.age]);
const sql = `
INSERT INTO users (id, name, age)
VALUES ?
ON DUPLICATE KEY UPDATE
name = VALUES(name),
age = VALUES(age)
`;
// mysql2 的 query 方法支持传入二维数组来替换 VALUES ?
await connection.query(sql, [values]);
}
方法三:使用事务 + 循环更新 (最安全/逻辑最简单)
如果你不熟悉复杂的 SQL 拼接,或者需要对每一条更新进行复杂的逻辑判断,可以使用事务(Transaction)包裹多条 UPDATE 语句。
适用场景: 数据量不大,或者必须保证每条更新的原子性。
javascript
const mysql = require('mysql2/promise');
async function batchUpdateTransaction(data) {
const connection = await mysql.createConnection({/* config */});
try {
await connection.beginTransaction();
for (const item of data) {
await connection.execute(
'UPDATE users SET name = ?, age = ? WHERE id = ?',
[item.name, item.age, item.id]
);
}
await connection.commit();
} catch (error) {
await connection.rollback();
throw error;
}
}
总结与对比
| 方法 | 优点 | 缺点 | 建议 |
|---|---|---|---|
| CASE WHEN | 标准 SQL,不依赖唯一键冲突,单次 IO | 拼接 SQL 逻辑复杂,数据量过大时 SQL 字符串超长 | 中等规模更新首选 |
| ON DUPLICATE KEY | 速度最快,代码最简洁 | 必须有主键/唯一索引,会意外插入不存在的数据 | 超大规模更新首选 |
| 事务循环 | 逻辑最清晰,支持复杂判断 | 数据库往返 IO 次数多,性能相对较低 | 小批量或逻辑复杂时使用 |
💡 进阶技巧:
如果批量更新的数据量达到 万级 以上:
- 分批执行: 不要一次性发 10 万条,建议每 500-1000 条作为一组进行批量操作。
- 临时表法: 先将数据
LOAD DATA或批量插入到一个临时表,然后使用UPDATE users JOIN temp_users ...的语法进行关联更新。这是处理百万级数据最快的方式。