使用 HAVING 查询耗时过长

Posted

技术标签:

【中文标题】使用 HAVING 查询耗时过长【英文标题】:Query using HAVING taking too long 【发布时间】:2021-10-02 13:52:20 【问题描述】:

我有两个表的查询——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;

返回所有属性 3 和 4 或 5 和 6 之和大于 3 的匹配 id。

这个特定的查询返回 900k 行,不出所料,在 phpmyadmin 中这个查询需要花费大量时间,因为我想它需要将结果格式化为一个表格,但它将查询计时为 0.0113 秒。

然而,当我通过 PHP 进行此查询时,它需要 15 秒,如果我将查询更改为 LIMIT 到只有 100 个结果,它几乎立即运行,让我相信唯一的可能性是正在传输的数据量是什么正在减慢它。

但是通过网络传输 1M 4 字节整数真的需要 15 秒吗?

是进一步限制查询以使其返回更少结果的唯一解决方案吗?

编辑

我的查询的解释结果

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 秒的查询,因为这是数据在网络上移动所需的时间,但如果可以优化查询或我的表,那是最好的解决方案

行数不是问题,如下查询

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;

只返回 24 行,但也需要大约 15 秒

【问题讨论】:

如果 phpMyAdmin 告诉您查询只用了 0.0113 秒,那么剩下的 15 秒就渲染了该页面 是的,我承认这个事实,我的问题是为什么我的 php 代码需要 15 秒才能运行,而无需进行任何渲染 永远没有理由向用户显示 990k 行。所以想想“用户需要什么?”。如果您确实需要向他显示所有数据,请使用分页或其他类型的过滤将要查询的行数减少到 100 以下。 @ClausBönnhoff 用户看不到这么多行,ID 用于内部计算,这又需要 15 秒,并且没有数据被绘制到屏幕上。正如您所提到的,是过滤 100k+ 行以下的唯一解决方案?意味着问题是通过网络发送这么多数据? ids 用于内部计算 - 那么为什么不在数据库中这样做并返回 its 结果 - 将 900k 行拉出当您实际上并不需要时的数据库是查询性能 101;您已经测试并诊断出行数是原因。 【参考方案1】:

phpMyAdmin 不会给你所有的结果, 它还使用默认 25 个结果的限制。

如果您通过更改“行数”选择框或在查询中键入限制来更改此限制,则运行查询将需要更多时间。

【讨论】:

【参考方案2】:

我认为如果你重写条件,至少你可能会发现一些东西。例如,我认为这与第二个示例(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 移到外部查询中,会发生什么?

那种东西。我无法运行它,所以我无法得出更准确的答案,我想知道。

【讨论】:

【参考方案3】:

时间安排可能有两个重要部分:定位行并决定发送哪些 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 BYLIMIT 是毫无意义的。如果添加ORDER BY,则必须收集所有结果,对它们进行排序,最后执行LIMIT

网络传输时间

通过网络传输数百万行(我在慢日志中看到 10.7MB)非常耗时,但几乎不需要 CPU 时间。

一个EXPLAIN 表示可能有280 万行;这是正确的吗?慢日志说大约有 16M 行被触及——这可能是因为两个表、join、group by 等。我的重新制定和索引应该显着减少 16M,从而减少经过的时间(在网络传输时间之前)。

923K 行“已发送”——客户端将如何处理这么多行。一般来说,我发现“发送”超过几千行表明设计不佳。

"take 15 seconds to transfer 1M 4 byte ints over the network" -- 这是经过的时间,不能加快除非发送更少的行。 (顺便说一句,它可能作为几位数字的字符串发送,加上每一行的开销;我不知道 10726615 是实际的网络字节还是只计算整数。)

“ids 用于内部计算”——你如何用 ids 计算?如果您在其他地方查找 id,也许您可​​以增加查询的复杂性,从而在访问网络之前做更多的工作;然后发送更少的数据?

如果您想进一步讨论,请提供SHOW CREATE TABLE。 (它可能有一些细节没有出现在您的简化表定义中。)

【讨论】:

以上是关于使用 HAVING 查询耗时过长的主要内容,如果未能解决你的问题,请参考以下文章

Mongodb 查询耗时过长

PostgreSQL 查询耗时过长

SQL 查询耗时过长

Microsoft SQL Server:错误的查询执行计划耗时过长

PostGIS 查询耗时过长。 >400ms

由于 where 子句中的 <>,查询耗时过长