You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express+MySQL:复杂查询的整洁代码实现咨询

编写Express+MySQL复杂查询整洁代码的实用技巧

1. 用参数化查询替代字符串拼接(安全+可读)

直接拼接SQL字符串不仅有SQL注入风险,还会让代码变得杂乱不堪。换成mysql2(推荐用promise版)的占位符语法,既安全又清晰:

exports.addAuthor = function(req, res, next) {
  const a = checkAuthor(req.body);
  if (!a.valid) {
    const myerr = new Error(a.msg);
    myerr.status = 400;
    return next(myerr);
  }

  // 检查作者是否存在的参数化查询
  const checkExistsQuery = `
    SELECT COUNT(*) AS nb 
    FROM authors 
    WHERE name = ? AND firstname = ?
  `;

  myPool.query(checkExistsQuery, [a.name, a.firstname], (err, results) => {
    if (err) return next(err);
    
    if (results[0].nb > 0) {
      const err = new Error('该作者已存在');
      err.status = 409;
      return next(err);
    }

    // 插入新作者的参数化查询
    const insertQuery = `
      INSERT INTO authors (name, firstname, bio) 
      VALUES (?, ?, ?)
    `;
    myPool.query(insertQuery, [a.name, a.firstname, a.bio], (err, result) => {
      if (err) return next(err);
      res.status(201).json({ id: result.insertId, ...a });
    });
  });
};

2. 拆分逻辑为独立函数,用async/await让代码线性化

把重复或复杂的查询逻辑抽成单独工具函数,再用async/await替代回调嵌套,代码会变得更易读、易维护:

// 工具函数:检查作者是否存在
const checkAuthorExists = (name, firstname) => {
  return new Promise((resolve, reject) => {
    const query = `SELECT COUNT(*) AS nb FROM authors WHERE name = ? AND firstname = ?`;
    myPool.query(query, [name, firstname], (err, results) => {
      if (err) return reject(err);
      resolve(results[0].nb > 0);
    });
  });
};

// 工具函数:插入新作者
const insertAuthor = (authorData) => {
  return new Promise((resolve, reject) => {
    const query = `INSERT INTO authors (name, firstname, bio) VALUES (?, ?, ?)`;
    myPool.query(query, [authorData.name, authorData.firstname, authorData.bio], (err, result) => {
      if (err) return reject(err);
      resolve({ id: result.insertId, ...authorData });
    });
  });
};

// 主业务函数
exports.addAuthor = async function(req, res, next) {
  try {
    const a = checkAuthor(req.body);
    if (!a.valid) {
      const myerr = new Error(a.msg);
      myerr.status = 400;
      throw myerr;
    }

    const exists = await checkAuthorExists(a.name, a.firstname);
    if (exists) {
      const err = new Error('该作者已存在');
      err.status = 409;
      throw err;
    }

    const newAuthor = await insertAuthor(a);
    res.status(201).json(newAuthor);
  } catch (err) {
    next(err);
  }
};

3. 用模板字符串格式化多行SQL

ES6的模板字符串(反引号)可以直接写多行SQL,保留换行和缩进,一眼就能看懂SQL的结构:

// 关联查询作者及其所有书籍的复杂SQL
const getAuthorWithBooks = async (authorId) => {
  const query = `
    SELECT a.*, b.title, b.publish_date, b.isbn
    FROM authors a
    LEFT JOIN books b ON a.id = b.author_id
    WHERE a.id = ?
    ORDER BY b.publish_date DESC
  `;
  const rows = await DB.query(query, [authorId]);
  
  // 整理结果,避免重复作者数据
  if (rows.length === 0) return null;
  const author = { ...rows[0], books: [] };
  rows.forEach(row => {
    if (row.title) {
      author.books.push({ title: row.title, publish_date: row.publish_date, isbn: row.isbn });
    }
  });
  return author;
};

4. 封装数据库基础操作

如果项目规模较大,建议把数据库的连接、查询等基础操作封装成工具类,减少重复代码:

// db.js - 数据库工具类
const mysql = require('mysql2/promise');

const pool = mysql.createPool({
  host: 'localhost',
  user: 'your-username',
  password: 'your-password',
  database: 'your-db-name'
});

class DB {
  static async query(sql, params = []) {
    const [rows] = await pool.execute(sql, params);
    return rows;
  }

  static async getOne(sql, params = []) {
    const rows = await this.query(sql, params);
    return rows[0];
  }
}

module.exports = DB;

之后业务代码里就可以简洁调用:

const DB = require('./db');

exports.getAuthor = async (req, res, next) => {
  try {
    const author = await DB.getOne(`SELECT * FROM authors WHERE id = ?`, [req.params.id]);
    if (!author) {
      const err = new Error('作者不存在');
      err.status = 404;
      throw err;
    }
    res.json(author);
  } catch (err) {
    next(err);
  }
};

5. 注释关键逻辑(别过度注释)

对于复杂的查询分支、数据整理逻辑,加简洁的注释说明意图,而不是重复代码本身:

// 按年份分组统计作者的书籍出版数量
const getAuthorBookStats = async (authorId) => {
  const query = `
    SELECT YEAR(b.publish_date) AS publish_year, COUNT(*) AS book_count
    FROM books b
    WHERE b.author_id = ?
    GROUP BY publish_year
    ORDER BY publish_year DESC
  `;
  const stats = await DB.query(query, [authorId]);
  
  // 补全年份空缺,确保统计数据连续
  const latestYear = stats.length > 0 ? stats[0].publish_year : new Date().getFullYear();
  const fullStats = [];
  for (let year = latestYear; year >= latestYear - 5; year--) {
    const existing = stats.find(s => s.publish_year === year);
    fullStats.push({ publish_year: year, book_count: existing ? existing.book_count : 0 });
  }
  return fullStats;
};

内容的提问来源于stack exchange,提问作者JPMous

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 10:36:29