UNION ALL 比 N 个查询慢

UNION ALL Slower than N queries

这个问题在 之后,我想在检索每一行时 select 多个字段的最大值。

UNION ALL 接受的答案非常有效,但我现在遇到了一些缩放问题。

为了提供一些背景信息,我的匹配项 table 中有超过 300 万行,WHERE 条件中使用的过滤器可以将此数据集减少到大约 5000-6000 行。我正在使用 PostgreSQL.

The query 大约需要 14-16 秒来处理。奇怪的是,如果我运行一次查询一个,需要150ms。

所以如果我的数学是正确的,这个查询的总持续时间应该是 150ms * 20(select 最大值的字段数)= 3 秒,而不是 16 ??

为什么整个查询要花这么多时间?

我对此有一些疑问:

PS:这是我使用的 Node.js 代码,如果您想以比 pastebin 的 500 行更具可读性的方式阅读查询:

const fields = [
  'match_players.kills',
  'match_players.deaths',
  'match_players.assists',
  'match_players.gold',
  'matches.game_duration',
  'match_players.minions',
  'match_players.kda',
  'match_players.damage_taken',
  'match_players.damage_dealt_champions',
  'match_players.damage_dealt_objectives',
  'match_players.kp',
  'match_players.vision_score',
  'match_players.critical_strike',
  'match_players.time_spent_living',
  'match_players.heal',
  'match_players.turret_kills',
  'match_players.killing_spree',
  'match_players.double_kills',
  'match_players.triple_kills',
  'match_players.quadra_kills',
  'match_players.penta_kills',
]

const query = fields
  .map((field) => {
    return `
  (SELECT
      '${field}' AS what,
      ${field} AS amount,
      match_players.win as result,
      matches.id,
      matches.date,
      matches.gamemode,
      match_players.champion_id
  FROM
      match_players
  INNER JOIN
      matches
  ON
      matches.id = match_players.match_id
  WHERE
      match_players.summoner_puuid = :puuid
      AND match_players.remake = 0
      AND matches.gamemode NOT IN (800, 810, 820, 830, 840, 850, 2000, 2010, 2020)
  ORDER BY
      ${field} DESC, matches.id
  LIMIT 
      1)
  `
  })
  .join('UNION ALL ')

const { rows } = await Database.rawQuery(query, { puuid })

非常感谢您的宝贵时间。

如果您的数据库引擎和 API 支持常见的 table 表达式(WITH 关键字),那么您可以先执行进行连接和过滤的查询,然后使用执行 UNION ALL:

的结果集
const query = `
    WITH base as (
        SELECT 
            ${fields.join()},
            match_players.win as result,
            matches.id,
            matches.date,
            matches.gamemode,
            match_players.champion_id
        FROM
            match_players
        INNER JOIN
            matches
        ON
            matches.id = match_players.match_id
        WHERE
            match_players.summoner_puuid = :puuid
            AND match_players.remake = 0
            AND matches.gamemode NOT IN (800, 810, 820, 830, 840, 850, 2000, 2010, 2020)
    )
    ` + fields.map((field) => `
      (SELECT
          '${field}' AS what,
          ${field.split(".").pop()} AS amount,
          result,
          id,
          date,
          gamemode,
          champion_id
      FROM
          base
      ORDER BY
          2 DESC, id
      LIMIT
          1)
      `).join(' UNION ALL ');