即使结果达到1k,查询时间也太长

时间:2019-03-20 08:41:36

标签: mysql

我已经做了一些测试来优化下面的查询,但是没有一个帮助。

我尝试过的是

  1. 添加额外的索引
  2. 通过检查IN子句中的其他属性来更改查询逻辑
  3. 在线查询优化工具(eversql等)的经过测试的建议

我正在使用的索引;

radacct (`_accttime`);
radacct (`username`);
radacct (`acctstoptime`,`_accttime`);

完整查询;

  (SELECT *
   FROM `radacct`
   WHERE (radacct._accttime > NOW() - INTERVAL 1.2 HOUR)
     AND radacct.acctstoptime IN
       (SELECT MAX(radacct.acctstoptime)
        FROM `radacct`
        GROUP BY radacct.username) )
UNION
  (SELECT *
   FROM `radacct`
   WHERE (radacct._accttime >= DATE_SUB(NOW(), INTERVAL 2 MONTH)
          AND radacct.acctstoptime IS NULL) )

当我自己执行上面的SELECT条语句时,它们仅花费几毫秒的时间。

我对IN子句有疑问。所以这是需要时间的查询

1 个答案:

答案 0 :(得分:1)

如我所见,您的问题是您的IN中的依赖子查询。显然,优化器不会得到子查询在技术上没有太大变化的信息。 (同样,查询可能不是最理想的)。本质上,子查询是针对每一行执行的(这很糟糕)。

现在,我们必须找出哪一部分触发它成为从属,因为它不是真的。我的第一个尝试是给它一个不同的别名:

IN (SELECT MAX(inner.acctstoptime) FROM radacct AS `inner` GROUP BY inner.username)

如果这还不足以使其独立,请使其成为完整的联接(INNER,这样将从结果中丢弃未联接的行[=非最大行]):

INNER JOIN (
       SELECT MAX(inner.accstoptime) as maxstoptime, inner.username 
       FROM `radacct` AS `inner` 
       GROUP BY inner.username
   ) sub ON (sub.maxstoptime=radacct.acctstoptime)

希望能达到目的。

由于您的结果中包含最多acctstoptime个用户行,因此在极少数情况下,如果有一行具有acctstoptime的行,则该行可能包含一个用户的多个行,而不是该用户的最大值,但与另一个用户的最大值匹配。在连接部分中,您可以在在线子句中添加另一个条件。在IN子查询中,您将删除显式组,并添加WHERE radacct.username=inner.username。 (这确实会使它成为一个显式的依赖子查询,但是优化程序可能能够处理它)

更新:由于沟通不畅...

所产生的带有联接的完整查询:

(SELECT DISTINCT radacct.*
 FROM radacct
 INNER JOIN (
        SELECT MAX(inner.accstoptime) as maxstoptime, inner.username 
        FROM `radacct` AS `inner` 
        GROUP BY inner.username
    ) sub ON (sub.maxstoptime=radacct.acctstoptime) 
  WHERE (_accttime > NOW() - INTERVAL 1.2 HOUR)
 ) 
 UNION 
  (SELECT *
   FROM `radacct`
   WHERE (_accttime >= DATE_SUB(NOW(),INTERVAL 2 MONTH)
          AND acctstoptime IS NULL)
  )

您仍然可以在ON子句中添加用户名比较。

此查询的作用是,它删除“ IN”选择器并强制加入中间结果(对于每个用户名,最大acctstoptime)。然后,且仅当acctstoptime是某个用户(或该用户,如果您添加用户名比较)的最大值,则该联接会将正常行连接到中间结果行。如果没有最大acctstoptime,因此没有联接“伙伴”,它将从结果中丢弃(由INNER引起,LEFT JOIN有点不足),因此仅保留行最长acctstoptime(在联盟的第一部分)。