使用 HAVING 的查询花费的时间太长
Query using HAVING taking too long
我有两个 table 的查询 -- matchoverview
id,home_id,away_id,日期,季节,结果
匹配属性
id, game_id, attribute_id, attribute_value
我的查询
select m.id from matchOverview m
join matchAttributes ma on ma.match_id=m.id and ma.attribute_id in (3,4,5,6)
group by m.id
having sum(case when ma.attribute_id in (3,4)
then ma.attribute_value end) > 3
or sum(case when ma.attribute_id in (5,6)
then ma.attribute_value end) > 3;
哪些returns匹配属性3和4或5和6之和大于3的id。
这个特定的查询 returns 900k 行,不出所料,在 phpmyadmin 中这个查询需要花费大量时间,因为我想它需要将结果格式化为 table,但它在查询时计时.0113 秒。
然而,当我在 PHP 上进行此查询时,它需要 15 秒,如果我将查询更改为 LIMIT 仅 100 个结果,它几乎立即运行,让我相信唯一的可能性是数量正在传输的数据是它变慢的原因。
但是通过网络传输 1M 4 字节整数真的需要 15 秒吗?
是否进一步限制查询以使其 returns 更少结果的唯一解决方案?
编辑
我的查询的 EXPLAIN 结果
id select_type table type key key key_len ref rows Extra
1 SIMPLE m index PRIMARY PRIMARY 4 NULL 2790717 Using index
1 SIMPLE ma ref match,attribute match 4 opta_matches2.m.id 2 Using where
我如何为 SQL 查询计时
$time_pre = microtime(true);
$quer = $db->query($sql);
$time_post = microtime(true);
$exec_time = $time_post - $time_pre;
来自慢查询日志的数据
# Thread_id: 15 Schema: opta_matches2 QC_hit: No
# Query_time: 15.594386 Lock_time: 0.000089 Rows_sent: 923962 Rows_examined: 15688514
# Rows_affected: 0 Bytes_sent: 10726615
我可以处理 15 秒的查询,因为这是数据在网络上移动所花费的时间,但如果查询或我的 table 可以优化,那就是最佳解决方案
行数不是问题,下面的查询
select m.id from matchOverview m
join matchAttributes ma on ma.match_id=m.id and ma.attribute_id in (1,2,3,4)
group by m.id
having sum(case when ma.attribute_id in (3,4)
then ma.attribute_value end) > 8
and sum(case when ma.attribute_id in (1,2)
then ma.attribute_value end) = 0;
returns 只有 24 行,但也需要 ~15 秒
phpMyAdmin 不会给你所有的结果,
它还使用限制为默认 25 个结果。
如果您通过更改“行数”select 框或在查询中键入限制来更改此限制,运行 查询将花费更多时间。
我想如果你重写条件,至少你可能会发现一些东西。例如,我认为这与第二个示例(24 个结果之一)相同;
SELECT
m.id
, at.total_12
, at.total_34
FROM matchOverview AS m
JOIN (
SELECT
m.id
, SUM(IF (ma.attribute_id IN(1,2), ma.attribute_value, 0)) AS total_12
, SUM(IF (ma.attribute_id IN(3,4), ma.attribute_value, 0)) AS total_34
FROM matchAttributes AS ma
WHERE m.id = ma.match_id
AND ma.attribute_id IN(1,2,3,4)
GROUP BY m.id
) AS at
WHERE at.total_12 > 0
AND at.total_34 > 8
它更冗长,但它可以帮助更容易地三角测量瓶颈的来源。
例如,如果上面的(工作)版本仍然很慢,那么 运行 内部查询与 GROUP BY
完好无损。还是慢?删除 GROUP BY
。将 GROUP BY/SUM
移动到外部查询中,会发生什么?
那种事。我不能 运行 所以我无法得出更准确的答案,我想知道。
时间安排可能有两个重要部分:定位行并决定发送哪些 ID;然后发送给他们。我会解决这两个问题。
这里有一种方法可以更好地分离查询(而不是网络)的经过时间:SELECT COUNT(*) FROM (...) AS x;
其中“...”是 1M 行查询。
加快查询速度
因为你没有真正使用 matchoverview
,让我们摆脱它:
select ma.match_id
from matchAttributes ma
WHERE ma.attribute_id in (3,4,5,6)
group by ma.match_id
having sum(case when ma.attribute_id in (3,4) then ma.attribute_value end) > 3
or sum(case when ma.attribute_id in (5,6) then ma.attribute_value end) > 3;
并有一个包含以下顺序列的复合索引:
INDEX(attribute_id, attribute_value, match_id)
至于快LIMIT
,那是因为它可以短停。但是没有 ORDER BY
的 LIMIT
是毫无意义的。如果你添加一个ORDER BY
,它必须收集所有结果,对它们进行排序,最后执行LIMIT
.
网络传输时间
通过网络传输数百万行(我在慢日志中看到 10.7MB)非常耗时,但实际上不需要 CPU 时间。
一个EXPLAIN
表示可能有280万行;这是正确的吗? slowlog 说大约有 16M 行被触及——这可能是因为两个 tables、join、group by 等。我的重新制定和索引应该显着减少 16M,因此减少经过的时间(在网络传输时间)。
923K 行“已发送”——客户端将如何处理这么多行。一般来说,我发现超过几千行“发送”表明设计不佳。
“通过网络传输 1M 4 字节整数需要 15 秒”——这是经过的时间,并且无法加快 除非发送更少的行。 (顺便说一句,它可能作为几位数字的字符串发送,加上每一行的开销;我不知道 10726615 是实际的网络字节还是只计算整数。)
"ids 用于内部计算" -- 如何使用 ids 进行计算?如果您在其他地方查找 ID,也许您可以增加查询的复杂性,从而在访问网络之前做更多的工作;然后传输更少的数据?
如需进一步讨论,请提供SHOW CREATE TABLE
。 (它可能有一些细节没有出现在您的简化 table 定义中。)
我有两个 table 的查询 -- matchoverview
id,home_id,away_id,日期,季节,结果
匹配属性
id, game_id, attribute_id, attribute_value
我的查询
select m.id from matchOverview m
join matchAttributes ma on ma.match_id=m.id and ma.attribute_id in (3,4,5,6)
group by m.id
having sum(case when ma.attribute_id in (3,4)
then ma.attribute_value end) > 3
or sum(case when ma.attribute_id in (5,6)
then ma.attribute_value end) > 3;
哪些returns匹配属性3和4或5和6之和大于3的id。
这个特定的查询 returns 900k 行,不出所料,在 phpmyadmin 中这个查询需要花费大量时间,因为我想它需要将结果格式化为 table,但它在查询时计时.0113 秒。
然而,当我在 PHP 上进行此查询时,它需要 15 秒,如果我将查询更改为 LIMIT 仅 100 个结果,它几乎立即运行,让我相信唯一的可能性是数量正在传输的数据是它变慢的原因。
但是通过网络传输 1M 4 字节整数真的需要 15 秒吗?
是否进一步限制查询以使其 returns 更少结果的唯一解决方案?
编辑
我的查询的 EXPLAIN 结果
id select_type table type key key key_len ref rows Extra
1 SIMPLE m index PRIMARY PRIMARY 4 NULL 2790717 Using index
1 SIMPLE ma ref match,attribute match 4 opta_matches2.m.id 2 Using where
我如何为 SQL 查询计时
$time_pre = microtime(true);
$quer = $db->query($sql);
$time_post = microtime(true);
$exec_time = $time_post - $time_pre;
来自慢查询日志的数据
# Thread_id: 15 Schema: opta_matches2 QC_hit: No
# Query_time: 15.594386 Lock_time: 0.000089 Rows_sent: 923962 Rows_examined: 15688514
# Rows_affected: 0 Bytes_sent: 10726615
我可以处理 15 秒的查询,因为这是数据在网络上移动所花费的时间,但如果查询或我的 table 可以优化,那就是最佳解决方案
行数不是问题,下面的查询
select m.id from matchOverview m
join matchAttributes ma on ma.match_id=m.id and ma.attribute_id in (1,2,3,4)
group by m.id
having sum(case when ma.attribute_id in (3,4)
then ma.attribute_value end) > 8
and sum(case when ma.attribute_id in (1,2)
then ma.attribute_value end) = 0;
returns 只有 24 行,但也需要 ~15 秒
phpMyAdmin 不会给你所有的结果, 它还使用限制为默认 25 个结果。
如果您通过更改“行数”select 框或在查询中键入限制来更改此限制,运行 查询将花费更多时间。
我想如果你重写条件,至少你可能会发现一些东西。例如,我认为这与第二个示例(24 个结果之一)相同;
SELECT
m.id
, at.total_12
, at.total_34
FROM matchOverview AS m
JOIN (
SELECT
m.id
, SUM(IF (ma.attribute_id IN(1,2), ma.attribute_value, 0)) AS total_12
, SUM(IF (ma.attribute_id IN(3,4), ma.attribute_value, 0)) AS total_34
FROM matchAttributes AS ma
WHERE m.id = ma.match_id
AND ma.attribute_id IN(1,2,3,4)
GROUP BY m.id
) AS at
WHERE at.total_12 > 0
AND at.total_34 > 8
它更冗长,但它可以帮助更容易地三角测量瓶颈的来源。
例如,如果上面的(工作)版本仍然很慢,那么 运行 内部查询与 GROUP BY
完好无损。还是慢?删除 GROUP BY
。将 GROUP BY/SUM
移动到外部查询中,会发生什么?
那种事。我不能 运行 所以我无法得出更准确的答案,我想知道。
时间安排可能有两个重要部分:定位行并决定发送哪些 ID;然后发送给他们。我会解决这两个问题。
这里有一种方法可以更好地分离查询(而不是网络)的经过时间:SELECT COUNT(*) FROM (...) AS x;
其中“...”是 1M 行查询。
加快查询速度
因为你没有真正使用 matchoverview
,让我们摆脱它:
select ma.match_id
from matchAttributes ma
WHERE ma.attribute_id in (3,4,5,6)
group by ma.match_id
having sum(case when ma.attribute_id in (3,4) then ma.attribute_value end) > 3
or sum(case when ma.attribute_id in (5,6) then ma.attribute_value end) > 3;
并有一个包含以下顺序列的复合索引:
INDEX(attribute_id, attribute_value, match_id)
至于快LIMIT
,那是因为它可以短停。但是没有 ORDER BY
的 LIMIT
是毫无意义的。如果你添加一个ORDER BY
,它必须收集所有结果,对它们进行排序,最后执行LIMIT
.
网络传输时间
通过网络传输数百万行(我在慢日志中看到 10.7MB)非常耗时,但实际上不需要 CPU 时间。
一个EXPLAIN
表示可能有280万行;这是正确的吗? slowlog 说大约有 16M 行被触及——这可能是因为两个 tables、join、group by 等。我的重新制定和索引应该显着减少 16M,因此减少经过的时间(在网络传输时间)。
923K 行“已发送”——客户端将如何处理这么多行。一般来说,我发现超过几千行“发送”表明设计不佳。
“通过网络传输 1M 4 字节整数需要 15 秒”——这是经过的时间,并且无法加快 除非发送更少的行。 (顺便说一句,它可能作为几位数字的字符串发送,加上每一行的开销;我不知道 10726615 是实际的网络字节还是只计算整数。)
"ids 用于内部计算" -- 如何使用 ids 进行计算?如果您在其他地方查找 ID,也许您可以增加查询的复杂性,从而在访问网络之前做更多的工作;然后传输更少的数据?
如需进一步讨论,请提供SHOW CREATE TABLE
。 (它可能有一些细节没有出现在您的简化 table 定义中。)