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

Node.js+Express如何将MySQL两表查询结果合并为指定JSON结构?

实现嵌套JSON结构的可行方案

针对你的需求,这里提供两种简单易实现的方案,基于mysql2的promise API(用async/await避免回调地狱,更适合新手):

方法1:单次JOIN查询后在代码中组装结构

先通过SQL JOIN关联两张表,再用JavaScript的reduce方法将查询结果整理成嵌套结构:

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

// 数据库连接配置,替换成你的实际信息
const dbConfig = {
  host: 'localhost',
  user: 'your_db_user',
  password: 'your_db_password',
  database: 'your_db_name'
};

app.get('/api/matchdays', async (req, res) => {
  let connection;
  try {
    // 建立数据库连接
    connection = await mysql.createConnection(dbConfig);
    
    // JOIN查询所有数据,保留matchdays和对应的scores字段
    const [rows] = await connection.execute(`
      SELECT m.matchdayid, m.matchdaydate, s.scoreid, s.player, s.score
      FROM matchdays m
      LEFT JOIN matchday_scores s ON m.matchdayid = s.matchdayid
      ORDER BY m.matchdayid
    `);

    // 组装嵌套JSON结构
    const nestedResult = rows.reduce((acc, row) => {
      // 查找当前matchday是否已在结果数组中
      const existingMatchday = acc.find(item => item.matchdayid === row.matchdayid);
      
      if (existingMatchday) {
        // 如果当前行有score数据,添加到对应matchday的scores数组
        if (row.scoreid) {
          existingMatchday.scores.push({
            scoreid: row.scoreid,
            player: row.player,
            score: row.score
          });
        }
      } else {
        // 新建matchday对象,初始化scores数组
        const newMatchday = {
          matchdayid: row.matchdayid,
          matchdaydate: row.matchdaydate,
          scores: []
        };
        // 如果当前行有score数据,加入数组
        if (row.scoreid) {
          newMatchday.scores.push({
            scoreid: row.scoreid,
            player: row.player,
            score: row.score
          });
        }
        acc.push(newMatchday);
      }
      return acc;
    }, []);

    res.json(nestedResult);
  } catch (err) {
    console.error('数据库查询错误:', err);
    res.status(500).json({ error: '获取数据失败' });
  } finally {
    // 确保数据库连接关闭
    if (connection) await connection.end();
  }
});

const PORT = 3000;
app.listen(PORT, () => console.log(`服务器运行在端口 ${PORT}`));

要点:

  • 使用LEFT JOIN保证即使没有对应scores的matchday也会被返回,此时scores数组为空
  • 通过reduce分组,将同一matchdayid的score数据合并到一个数组中
  • 用async/await简化异步流程,避免回调嵌套

方法2:两次查询后关联数据

先查询所有matchdays,再查询所有scores并按matchdayid分组,最后将两者关联,逻辑更直观:

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

const dbConfig = {
  host: 'localhost',
  user: 'your_db_user',
  password: 'your_db_password',
  database: 'your_db_name'
};

app.get('/api/matchdays', async (req, res) => {
  let connection;
  try {
    connection = await mysql.createConnection(dbConfig);
    
    // 第一步:查询所有matchdays基础数据
    const [matchdays] = await connection.execute('SELECT matchdayid, matchdaydate FROM matchdays ORDER BY matchdayid');
    
    // 第二步:查询所有scores数据
    const [scores] = await connection.execute('SELECT matchdayid, scoreid, player, score FROM matchday_scores');
    
    // 将scores转成以matchdayid为键的映射对象,方便快速查找
    const scoreMap = scores.reduce((map, score) => {
      if (!map[score.matchdayid]) {
        map[score.matchdayid] = [];
      }
      map[score.matchdayid].push({
        scoreid: score.scoreid,
        player: score.player,
        score: score.score
      });
      return map;
    }, {});

    // 给每个matchday添加对应的scores数组
    const nestedResult = matchdays.map(matchday => ({
      ...matchday,
      scores: scoreMap[matchday.matchdayid] || []
    }));

    res.json(nestedResult);
  } catch (err) {
    console.error('数据库查询错误:', err);
    res.status(500).json({ error: '获取数据失败' });
  } finally {
    if (connection) await connection.end();
  }
});

const PORT = 3000;
app.listen(PORT, () => console.log(`服务器运行在端口 ${PORT}`));

要点:

  • 拆分查询逻辑,新手更容易理解和调试
  • 用reduce构建score映射表,避免循环中重复查询数据库(性能更优)
  • 同样通过LEFT JOIN的思路,用|| []保证没有scores的matchday返回空数组

注意事项

  1. 替换代码中的dbConfig为你的实际数据库连接信息
  2. 根据matchday_scores表的实际字段调整代码中的字段名(比如如果你的表没有player字段,改成你实际的字段)
  3. 生产环境中建议使用数据库连接池(mysql2.createPool)而不是每次请求新建连接,提升性能
  4. 可以给SQL语句添加索引(比如给matchday_scores.matchdayid加索引),优化查询速度

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 16:45:03