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 ??
为什么整个查询要花这么多时间?
我对此有一些疑问:
- 进行 20 次查询并汇总最终结果是否更好?
- 我可以使用一些索引来加快查询速度吗?
- 是否可以只创建 WHERE 过滤器 + JOIN 一次,而不是在我的所有查询中都这样做?
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 ');
这个问题在
UNION ALL
接受的答案非常有效,但我现在遇到了一些缩放问题。
为了提供一些背景信息,我的匹配项 table 中有超过 300 万行,WHERE
条件中使用的过滤器可以将此数据集减少到大约 5000-6000 行。我正在使用 PostgreSQL.
The query 大约需要 14-16 秒来处理。奇怪的是,如果我运行一次查询一个,需要150ms。
所以如果我的数学是正确的,这个查询的总持续时间应该是 150ms * 20(select 最大值的字段数)= 3 秒,而不是 16 ??
为什么整个查询要花这么多时间?
我对此有一些疑问:
- 进行 20 次查询并汇总最终结果是否更好?
- 我可以使用一些索引来加快查询速度吗?
- 是否可以只创建 WHERE 过滤器 + JOIN 一次,而不是在我的所有查询中都这样做?
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 ');