Node.js+SQL技术疑问:Insert是否比Select更快?及MySQL去重插入问题
嘿,我来帮你解答这两个Node.js结合SQL的问题:
问题一:Insert查询是否比Select查询速度更快?
这个没法一概而论,得看具体场景和查询类型:
- 简单场景对比:如果是单条Insert(比如插入一行数据)和基于主键的Select(比如
SELECT * FROM table WHERE id = 1),通常Select会更快。因为Insert不仅要写入数据,还要维护索引、更新事务日志、确保数据持久化(比如InnoDB的刷盘策略),而主键Select直接通过索引定位,甚至可能命中缓存,几乎是瞬时的。 - 复杂场景对比:如果是复杂的Select查询(比如多表关联、全表扫描、带多个聚合函数的查询),那Insert(哪怕是单条)可能会更快。因为这类Select需要扫描大量数据、做计算,耗时会远超简单的Insert操作。
- 批量操作差异:如果是批量Insert(比如一次插入几百上千条),开启事务后批量写入的速度会大幅提升,这时候可能比一些中等复杂度的Select更快。但和简单主键Select比,还是会慢一些。
- 影响因素:数据库的配置(比如缓存大小、写入策略)、索引数量(Insert要更新所有相关索引,索引越多越慢)、事务隔离级别、数据量大小都会影响两者的速度。
总结:没有绝对的谁更快,得结合具体的查询类型、数据量和数据库配置来判断。
问题二:如何插入数组中不重复且数据库不存在的条目?
要解决这个问题,我们可以分两步走:先对数组内的重复对象去重,再排除数据库中已存在的条目,最后批量插入。当然也可以借助数据库的特性来简化流程,我给你两种方案:
方案一:先前端去重,再查询数据库排除已存在条目,最后批量插入
这种方案适合没有办法修改数据库索引的情况:
- 数组内去重:首先定义“重复对象”的判断标准(比如
name+sname+url的组合唯一),然后过滤数组:
async function data_name(param) { var data = []; data.push({ name: param, sname: 'sname', url: 'url', report: 'report' }); return data; } // 获取数据并去重 const rawData = await data_name('name...'); // 用Map去重,键为唯一组合值 const uniqueData = [...new Map(rawData.map(item => [`${item.name}-${item.sname}-${item.url}`, item])).values()];
- 查询数据库中已存在的条目:用批量查询的方式找出数据库里已有的记录,避免循环查询的性能问题:
// 假设你用的是mysql2库,connection是已建立的连接 const placeholders = uniqueData.map((_, idx) => '(?, ?, ?)').join(','); const queryValues = uniqueData.flatMap(item => [item.name, item.sname, item.url]); // 查询已存在的记录 const [existingRows] = await connection.execute( `SELECT name, sname, url FROM your_table WHERE (name, sname, url) IN (${placeholders})`, queryValues ); // 把已存在的组合存入Set,方便快速判断 const existingKeys = new Set(existingRows.map(row => `${row.name}-${row.sname}-${row.url}`));
- 过滤出需要插入的条目并批量插入:
const toInsert = uniqueData.filter(item => { const key = `${item.name}-${item.sname}-${item.url}`; return !existingKeys.has(key); }); // 批量插入 if (toInsert.length > 0) { const insertPlaceholders = toInsert.map((_, idx) => '(?, ?, ?, ?)').join(','); const insertValues = toInsert.flatMap(item => [item.name, item.sname, item.url, item.report]); await connection.execute( `INSERT INTO your_table (name, sname, url, report) VALUES ${insertPlaceholders}`, insertValues ); }
方案二:利用数据库唯一索引+批量插入(更高效)
如果可以修改数据库表结构,推荐这种方案,让数据库帮我们处理重复问题,减少代码复杂度和查询次数:
- 给表添加联合唯一索引:确保表对
name+sname+url组合有唯一约束,这样数据库会自动拒绝重复的插入:
ALTER TABLE your_table ADD UNIQUE INDEX idx_name_sname_url (name, sname, url);
- 数组内去重+直接批量插入:先对数组去重,然后用
INSERT IGNORE或者ON DUPLICATE KEY UPDATE语句插入,数据库会自动忽略已存在的条目:
const rawData = await data_name('name...'); const uniqueData = [...new Map(rawData.map(item => [`${item.name}-${item.sname}-${item.url}`, item])).values()]; if (uniqueData.length > 0) { const insertPlaceholders = uniqueData.map((_, idx) => '(?, ?, ?, ?)').join(','); const insertValues = uniqueData.flatMap(item => [item.name, item.sname, item.url, item.report]); // INSERT IGNORE会忽略重复条目,不抛出错误 await connection.execute( `INSERT IGNORE INTO your_table (name, sname, url, report) VALUES ${insertPlaceholders}`, insertValues ); // 如果你需要更新已存在条目的report字段,可以用下面的语句: // await connection.execute( // `INSERT INTO your_table (name, sname, url, report) VALUES ${insertPlaceholders} ON DUPLICATE KEY UPDATE report = VALUES(report)`, // insertValues // ); }
这个方案更高效,因为省去了一次查询数据库的操作,而且数据库层面的约束更可靠。
内容的提问来源于stack exchange,提问作者Grzegorz Kubiszyn
相关产品推荐
相关产品推荐

