← MySQL 创建数据表 MySQL 更新数据 →

Node.js使用mysql2执行MySQL INSERT INTO插入数据完整教程(单条/批量/占位符/错误处理)

著
原创 2026-10-13 Node.js 已有人查阅

1.先理解INSERT INTO在Node.js里的位置

MySQL的INSERT INTO负责向表中写入新行。Node.js本身不认识SQL,它通过mysql2这类驱动把SQL字符串发送给MySQL服务端,服务端解析并执行,之后把执行结果回传给Node.js回调或Promise。

这条链路可以拆成四步:

  1. Node.js应用组装SQL语句与参数;

  2. mysql2通过已建立的TCP连接把语句发给MySQL;

  3. MySQL解析、校验、写入,并返回结果对象;

  4. Node.js根据结果对象判断成功与否,决定后续逻辑。

很多初学者只关注“能不能去”,却忽略了结果对象里携带的信息。实际上,affectedRows和insertId才是判断写入是否真正生效的关键依据。下面这张图可以帮助理解整体数据流向:

2.动手前的环境确认

在写插入代码之前,有几项基础条件需要先落实,否则后面报错会很难定位:

  • Node.js已安装,node-v能正常输出版本号;

  • MySQLServer已安装并处于运行状态;

  • 已经创建好目标数据库,例如studentdb;

  • 项目中已安装mysql2依赖。

安装mysql2的命令如下:

npm install mysql2

这里建议直接使用mysql2,而不是旧的mysql包。mysql2在保持API兼容的同时,原生支持Promise、预处理语句和更完善的类型处理,后续做工程化封装会省很多事。

3.建表:插入的前提

如果目标表还不存在,插入语句一定失败。以学生表为例,建表语句如下:

CREATE TABLE Students (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50),
    age INT,
    city VARCHAR(50)
);

四个字段的职责很清晰:

字段 含义
id 学生唯一编号,自增主键
name 学生姓名
age 学生年龄
city 所在城市

id使用AUTO_INCREMENT,意味着插入时不需要手动传值,MySQL会自动分配递增编号。这一点在后续读取insertId时非常关键。

4.建立数据库连接

先看一个最小可用的连接示例,文件可以命名为code号_db.js:

const mysql = require('mysql2');

const connection = mysql.createConnection({
  host: 'localhost',
  user: 'root',
  password: 'password',
  database: 'studentdb'
});

module.exports = connection;

把连接单独抽出来,是为了后续插入、查询、更新都能复用,避免每写一个功能就重复一遍连接配置。实际项目中,连接信息应放入环境变量,不要硬编码在源码里。

5.单条插入:最基础的写入形式

SQL语法结构如下:

INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...);

INSERT INTO指定目标表和列,VALUES给出对应列的值。列的顺序和值的顺序必须一一对应,否则会写错字段。

Node.js中把这条语句存成字符串,再交给query()执行:

const sql =
  "INSERT INTO Students (name, age, city) VALUES ('Anirudh', 21, 'Noida')";

connection.query(sql, (err, result) => {
  if (err) throw err;
  console.log('记录插入成功');
  console.log(result);
});

执行成功后,控制台会输出类似下面的结果对象:

ResultSetHeader {
  fieldCount: 0,
  affectedRows: 1,
  insertId: 1,
  warningStatus: 0
}

其中affectedRows:1表示影响了一行,insertId:1表示新记录的id为1。这两个字段后面会反复用到。

6.批量插入:一次写入多行

单条插入适合零散数据,但遇到批量录入时,逐条执行会产生大量网络往返。更合理的做法是用一条INSERT INTO...VALUES(...),(...),(...)语句完成。

const sql = `
INSERT INTO Students (name, age, city)
VALUES
 ('Priya', 22, 'Mumbai'),
 ('Amit', 20, 'Jaipur'),
 ('Neha', 23, 'Pune'),
 ('Rohan', 24, 'Noida'),
 ('Sneha', 21, 'Lucknow'),
 ('Arjun', 22, 'Chandigarh'),
 ('Ananya', 20, 'Kolkata'),
 ('Vikash', 23, 'Bangalore'),
 ('Pooja', 21, 'Hyderabad')
`;

connection.query(sql, (err, result) => {
  if (err) throw err;
  console.log('批量插入成功');
  console.log(result.affectedRows);
});

输出9,说明九条记录全部写入。此时表中数据如下:

id name age city
1 Anirudh 21 Noida
2 Priya 22 Mumbai
3 Amit 20 Jaipur
4 Neha 23 Pune
5 Rohan 24 Noida
6 Sneha 21 Lucknow
7 Arjun 22 Chandigarh
8 Ananya 20 Kolkata
9 Vikash 23 Bangalore
10 Pooja 21 Hyderabad

id从1到10连续生成,正是AUTO_INCREMENT在起作用。

经验提醒:批量插入并非越大越好。单条SQL过长会受max_allowed_packet限制,实际项目中建议按每批500到1000行切分,既能减少往返,又不会撑爆包体。

7.占位符:安全写入的正确姿势

直接把变量拼进SQL字符串,是SQL注入的温床。mysql2提供?占位符机制,把语句和值分开传递:

const sql =
  "INSERT INTO Students (name, age, city) VALUES (?, ?, ?)";
const values = ['Sonam', 24, 'Pune'];

connection.query(sql, values, (err, result) => {
  if (err) throw err;
  console.log('记录插入成功');
});

三个?分别对应name、age、city,values数组按顺序填充。mysql2会在底层做转义处理,用户输入中的引号、分号不会破坏语句结构。

这里有一个容易被忽略的点:占位符不仅能防注入,还能让MySQL复用执行计划。当同一条INSERT需要反复执行时,使用预处理语句(preparedstatement)可以省去重复解析的开销。mysql2的execute()方法就是为此设计的:

connection.execute(sql, values, (err, result) => {
  if (err) throw err;
  console.log('执行成功');
});

query()与execute()的差别在于:前者每次把完整SQL发给服务端,后者会先准备语句再传参。写入频率高的场景,优先考虑execute()。

8.读取执行结果:affectedRows与insertId

插入是否成功,不能只看有没有抛异常,还要看结果对象:

connection.query(sql, values, (err, result) => {
  if (err) throw err;

  console.log('Affected Rows:', result.affectedRows);
  console.log('Inserted ID:', result.insertId);
});

输出:

Affected Rows: 1
Inserted ID: 11
  • affectedRows表示本次INSERT影响的行数,单条插入通常为1,批量插入等于实际写入行数;

  • insertId表示自增主键新生成的值,只在表含自增列时有意义。

这两个值在业务中很有用。比如下单成功后需要拿到订单id返回给前端,就可以直接取result.insertId,不必再查一次数据库。

9.错误处理:让程序体面地失败

插入过程中可能遇到表不存在、字段名写错、连接断开等问题。mysql2会把错误信息放在回调的第一个参数err中:

const sql =
  "INSERT INTO StudentData (name, age) VALUES ('Anirudh', 21)";

connection.query(sql, (err, result) => {
  if (err) {
    console.log('Error:', err.message);
    return;
  }

  console.log('记录插入成功');
});

输出:

Error: Table 'studentdb.studentdata' doesn't exist

err.message给出了可读的错误描述,return阻止回调继续往下执行。实际项目中不应只打印日志,还要根据错误类型做区分处理:连接类错误可重试,语法类错误应直接暴露给开发阶段修复,唯一键冲突则要转换成业务提示。

下面这张小图可以放在错误处理章节作为示意:

10.现在写法:Promise与async/await

回调写法在多层嵌套时可读性差。mysql2提供mysql2/promise,可以直接用async/await:

const mysql = require('mysql2/promise');

async function addStudent(name, age, city) {
  const connection = await mysql.createConnection({
    host: 'localhost',
    user: 'root',
    password: 'password',
    database: 'studentdb'
  });

  try {
    const sql =
      'INSERT INTO Students (name, age, city) VALUES (?, ?, ?)';
    const [result] = await connection.execute(sql, [name, age, city]);
    return result.insertId;
  } finally {
    await connection.end();
  }
}

这种写法在Express、Koa、NestJS等框架里更常见,配合try/catch/finally能确保连接被释放。

11.事务写入:多条INSERT要么全成要么全败

当一次业务需要写入多张表,比如先插学生再插选课记录,就必须用事务保证一致性:

const connection = await mysql.createConnection({ /* ... */ });

try {
  await connection.beginTransaction();

  const [studentResult] = await connection.execute(
    'INSERT INTO Students (name, age, city) VALUES (?, ?, ?)',
    ['Kiran', 22, 'Delhi']
  );

  await connection.execute(
    'INSERT INTO Enrollments (student_id, course) VALUES (?, ?)',
    [studentResult.insertId, 'MySQL']
  );

  await connection.commit();
} catch (err) {
  await connection.rollback();
  throw err;
}

事务中任何一步失败,rollback()会撤销已写入的数据,避免出现学生记录存在但选课记录缺失的脏数据。

12.工程化建议与本节知识要点

知识要点

  • INSERT INTO的列与值必须顺序对应;

  • 批量插入用多组VALUES,减少网络往返;

  • 占位符?是防注入的基本手段,高频写入优先execute();

  • affectedRows判断影响行数,insertId获取自增主键;

  • 错误要分类处理,不能只console.log;

  • 多表写入必须用事务包裹。

个人建议

连接池比单连接更适合生产环境。mysql.createPool()可以复用连接,避免每次请求都经历握手开销。写入密集的应用中,连接池配合execute()能明显降低延迟。

插入前对数据进行类型校验很有必要。MySQL对类型不匹配的容忍度较高,比如字符串'21'写入INT列会被隐式转换,看似成功,实则埋下隐患。在应用层用校验库先过滤,比事后排查数据问题成本低得多。

13.练习思考题

  1. 用execute()改写第5节的单条插入,观察结果对象是否一致。

  2. 构造一条包含单引号的姓名,如O'Brien,分别用字符串拼接和占位符插入,对比结果。

  3. 在事务中故意让第二条INSERT引用不存在的表,验证rollback()是否生效。

  4. 查询Students表,确认批量插入后的id是否连续,思考如果中途删除一行再插入,id会如何变化。

INSERT INTO看似只是“写数据”,但真正落到项目里,涉及连接管理、参数绑定、结果解析、异常分支和事务边界。把这几层都处理清楚,写入逻辑才算可靠。mysql2提供的占位符、Promise接口和连接池,正是为了把这些环节做得更稳。写插入代码时,多花几分钟确认结果对象和错误分支,比事后补数据要划算得多。

← MySQL 创建数据表 MySQL 更新数据 →
分享笔记 (共有 篇笔记)
验证码:
微信公众号