daily-share icon indicating copy to clipboard operation
daily-share copied to clipboard

SQL 优化记录

Open yaogengzhu opened this issue 1 year ago • 0 comments

我写的

async function getTeamList(req, res) {
  const { user } = req;
  let { page, pageSize } = req.body;
  page = page || 1;
  pageSize = pageSize || 10;
  try {
    // 查询1: 团队总数
    const total = await knex('user')
      .count('* as total')
      .where('parent_id', req.user.id);
    // 查询2: 今日新增
    const today = await knex('user')
      .count('* as today')
      .where('parent_id', req.user.id)
      .whereRaw('DATE(created_at) = DATE(NOW())');
    // 查询3: 昨日新增
    const yesterday = await knex('user')
      .count('* as yesterday')
      .where('parent_id', req.user.id)
      .whereRaw('DATE(created_at) = DATE(NOW() - INTERVAL 1 DAY)');
    // 查询4: 本月新增
    const thisMonth = await knex('user')
      .count('* as this_month')
      .where('parent_id', req.user.id)
      .whereRaw(
        'MONTH(created_at) = MONTH(NOW()) AND YEAR(created_at) = YEAR(NOW())',
      );
    const teamInfo = {
      total: total[0].total,
      today: today[0].today,
      yesterday: yesterday[0].yesterday,
      thisMonth: thisMonth[0].this_month,
    };
    // 查询5: 团队列表
    const result = await knex('user')
      .select('*')
      .where('parent_id', user.id)
      .limit(pageSize)
      .offset((page - 1) * pageSize);
    if (result.length != 0) {
      for (let i = 0; i < result.length; i++) {
        // 查询7天内拉新
        const sevenDay = await knex('user')
          .count('* as count')
          .where('parent_id', result[i].id)
          .whereRaw('created_at > DATE_SUB(NOW(), INTERVAL 7 DAY)');
        result[i].sevenDay = sevenDay[0].count;
        // 七天出单
        const sevenDayOrder = await knex('sys_orders as so')
          .join('command_task as ct', 'ct.command_id', 'so.command_id')
          .count('* as count')
          .where('ct.user_id', result[i].id)
          .whereRaw('so.pay_time > DATE_SUB(NOW(), INTERVAL 7 DAY)');
        result[i].sevenDayOrder = sevenDayOrder[0].count;
      }
    }
    res.send({
      code: 0,
      message: 'ok',
      data: result,
      teamInfo,
    });
  } catch (e) {
    res.send({
      code: 1,
      message: '获取团队列表失败',
    });
    logger.info(e);
  }
}

优化的语句

async function getTeamList(req, res) {
  let { page, pageSize } = req.body;
  page = page || 1;
  pageSize = pageSize || 10;

  // 提取重复的查询条件
  const parentIdCondition = { parent_id: req.user.id };

  try {
    const [total, today, yesterday, thisMonth, result] = await Promise.all([
      // 查询1: 团队总数
      knex('user').count('* as total').where(parentIdCondition),

      // 查询2: 今日新增
      knex('user')
        .count('* as today')
        .where(parentIdCondition)
        .whereRaw('DATE(created_at) = DATE(NOW())'),

      // 查询3: 昨日新增
      knex('user')
        .count('* as yesterday')
        .where(parentIdCondition)
        .whereRaw('DATE(created_at) = DATE(NOW() - INTERVAL 1 DAY)'),

      // 查询4: 本月新增
      knex('user')
        .count('* as this_month')
        .where(parentIdCondition)
        .whereRaw(
          'MONTH(created_at) = MONTH(NOW()) AND YEAR(created_at) = YEAR(NOW())',
        ),

      // 查询5: 团队列表
      knex('user')
        .select('*')
        .where(parentIdCondition)
        .limit(pageSize)
        .offset((page - 1) * pageSize),
    ]);

    const teamInfo = {
      total: total[0].total,
      today: today[0].today,
      yesterday: yesterday[0].yesterday,
      thisMonth: thisMonth[0].this_month,
    };

    if (result.length !== 0) {
      const userIds = result.map((user) => user.id);
      // 查询7天内拉新和7天出单
      const [sevenDayCounts, sevenDayOrderCounts] = await Promise.all([
        knex('user')
          .select('parent_id')
          .count('* as count')
          .whereIn('parent_id', userIds)
          .whereRaw('created_at > DATE_SUB(NOW(), INTERVAL 7 DAY)')
          .groupBy('parent_id'),

        knex('sys_orders as so')
          .join('command_task as ct', 'ct.command_id', 'so.command_id')
          .select('ct.user_id')
          .count('* as count')
          .whereIn('ct.user_id', userIds)
          .whereRaw('so.pay_time > DATE_SUB(NOW(), INTERVAL 7 DAY)')
          .groupBy('ct.user_id'),
      ]);

      const sevenDayMap = new Map(
        sevenDayCounts.map((item) => [item.parent_id, item.count]),
      );
      const sevenDayOrderMap = new Map(
        sevenDayOrderCounts.map((item) => [item.user_id, item.count]),
      );

      result.forEach((user) => {
        user.sevenDay = sevenDayMap.get(user.id) || 0;
        user.sevenDayOrder = sevenDayOrderMap.get(user.id) || 0;
      });
    }

    res.send({
      code: 0,
      message: 'ok',
      data: result,
      teamInfo,
    });
  } catch (e) {
    res.send({
      code: 1,
      message: '获取团队列表失败',
    });
    logger.info(e);
  }
}

总结: 这里我们使用了 Promise.all 来并行执行多个查询,这样可以减少总的查询时间。同时,我们将查询7天内拉新和7天出单的操作合并为一个查询,并使用 Map 对象来存储结果,以便在后续的循环中快速查找。

注意:这里的优化主要是减少了查询次数,但实际上查询效率还是取决于数据库的性能和表的索引设置。如果查询仍然很慢,建议检查数据库性能和索引配置。

yaogengzhu avatar Feb 19 '24 06:02 yaogengzhu