2013-03-12 185 views
2

我的查询生成一些关于加速,上次和平均速度的报告。 这是我的查询:查询优化 - 花费太长时间并停止服务器

Select 
    r1 . *, r2.name, r2.notes, r2.serial 
From 
    (SELECT 
     k.idgps_unit, 
      MIN(k.dt) AS DT_Start, 
      MIN(CASE 
       WHEN k.RowNumber = 1 THEN k.Lat 
      END) AS Latitude_Start, 
      MIN(CASE 
       WHEN k.RowNumber = 1 THEN k.Long 
      END) AS Longitude_Start, 
      MIN(CASE 
       WHEN k.RowNumber = 1 THEN k.Speed_kmh 
      END) AS Speed_Start, 
      MAX(k.dt) AS dt_end, 
      MIN(CASE 
       WHEN k.RowNumber = MaxRowNo THEN k.Lat 
      END) AS Latitude_End, 
      MIN(CASE 
       WHEN k.RowNumber = MaxRowNo THEN k.Long 
      END) AS Longitude_End, 
      MIN(CASE 
       WHEN k.RowNumber = MaxRowNo THEN k.Speed_kmh 
      END) AS Speed_End, 
      AVG(Speed_kmh) AS Average_Speed 
    FROM 
     (SELECT 
     gps_unit_location . *, 
      @i:=CASE 
       WHEN Speed_Kmh > 80 AND @b = 0 THEN @i + 1 
       ELSE @i 
      END AS IntervalID, 
      @r:=CASE 
       WHEN Speed_Kmh > 80 AND @b = 0 THEN 1 
       ELSE @r + 1 
      END AS RowNumber, 
      @b:=CASE 
       WHEN Speed_Kmh > 80 THEN 1 
       ELSE 0 
      END AS IntervalCheck 
    FROM 
     gps_unit_location, (SELECT @i:=0) i, (SELECT @r:=0) r, (SELECT @b:=0) b 
    ORDER BY dt , idgps_unit_location) k 
    INNER JOIN (SELECT 
     IntervalID, MAX(RowNumber) AS MaxRowNo 
    FROM 
     (SELECT 
     gps_unit_location . *, 
      @i:=CASE 
       WHEN Speed_Kmh > 80 AND @b = 0 THEN @i + 1 
       ELSE @i 
      END AS IntervalID, 
      @r:=CASE 
       WHEN Speed_Kmh > 80 AND @b = 0 THEN 1 
       ELSE @r + 1 
      END AS RowNumber, 
      @b:=CASE 
       WHEN Speed_Kmh > 80 THEN 1 
       ELSE 0 
      END AS IntervalCheck 
    FROM 
     gps_unit_location, (SELECT @i:=0) i, (SELECT @r:=0) r, (SELECT @b:=0) b 
    ORDER BY dt , idgps_unit_location) d 
    WHERE 
     IntervalCheck = 1 
    GROUP BY IntervalID) MaxInt ON MaxInt.IntervalID = k.IntervalID 
    WHERE 
     k.IntervalCheck = 1 
      and k.idgps_unit in (SELECT 
       idgps_unit 
      FROM 
       instafleet.gps_unit 
      where 
       id_customer = (select 
         idcustomer 
        from 
         user 
        where 
         iduser = 14)) 
    GROUP BY k.IntervalID , k.idgps_unit) r1 
     Inner join 
    gps_unit r2 ON r1.idgps_unit = r2.idgps_unit 

目前,它需要3分钟783723条记录。我认为适当的指标可能会有所帮助;虽然经过一些试验和错误,但我无法弄清楚。如果你认为你可以提供帮助,并需要一些额外的信息 - 我会很高兴你提供给你。

解释 Explain

结果 Result

+3

这是一个很长的查询别人来优化您的免费重写。你可以把它分解成更小的查询来隔离问题吗? – 2013-03-12 17:17:31

+1

Marcus,我认为添加合适的索引可以解决它的性能问题..不需要重做它...我可能是错的:) – Andrew 2013-03-12 17:20:39

+0

您正在使用哪种GUI软件? – dynamic 2013-03-20 18:33:47

回答

2

添加一个索引有助于在许多情况下,但你有一个子查询加盟另一子查询,在当前表没有索引可以帮助你加快。您可以在这里使用索引的唯一方法是创建临时表。

正如Markus指出的那样,您需要将您的查询分解为几个较小的查询,这些查询将结果存储在临时表中。比你可以添加索引给他们,并希望加快你的查询。将大问题分解为几个较小问题的另一个好处是,您可以更好地分析哪个部分是较慢的问题并修复问题。

您还使用了一次子查询两次,这对性能不利,因为结果未被缓存。

这里是你如何能做到这一点的例子:如果在嵌套查询(S)的字节数超过了缓冲池的大小(检查innodb_buffer_pool_size)查询将采取

DROP TEMPORARY TABLE IF EXISTS tmp_k; 
CREATE TEMPORARY TABLE tmp_k 
    ENGINE=Memory 
SELECT 
    gps_unit_location.*, 
    @i:= IF(((Speed_Kmh > 80) AND (@b = 0)), @i + 1, @i) AS IntervalID, 
    @r:= IF(((Speed_Kmh > 80) AND (@b = 0)), 1, @r + 1) AS RowNumber, 
    @b:= IF((Speed_Kmh > 80), 1, 0) AS IntervalCheck 
FROM 
    gps_unit_location, 
    (SELECT @i:=0) i, 
    (SELECT @r:=0) r, 
    (SELECT @b:=0) b 
ORDER BY 
    dt, 
    idgps_unit_location; 

ALTER TABLE tmp_k ADD INDEX (IntervalID); 

DROP TEMPORARY TABLE IF EXISTS tmp_max; 
CREATE TEMPORARY TABLE tmp_max 
    ENGINE=Memory 
SELECT 
    IntervalID, 
    MAX(RowNumber) AS MaxRowNo 
FROM 
    temp_k 
WHERE 
    IntervalCheck = 1 
GROUP BY 
    IntervalID; 

ALTER TABLE tmp_max ADD INDEX (IntervalID); 

SELECT 
    k.idgps_unit, 
    MIN(k.dt) AS DT_Start, 
    MIN(IF(k.RowNumber = 1, k.Lat, NULL)) AS Latitude_Start, 
    MIN(IF(k.RowNumber = 1, k.Long, NULL)) AS Longitude_Start, 
    MIN(IF(k.RowNumber = 1, k.Speed_kmh, NULL) AS Speed_Start, 
    MAX(k.dt) AS DT_End, 
    MIN(IF(k.RowNumber = m.MaxRowNo, k.Lat, NULL)) AS Latitude_End 
    MIN(IF(k.RowNumber = m.MaxRowNo, k.Long, NULL)) AS Longitude_End 
    MIN(IF(k.RowNumber = m.MaxRowNo, k.Speed_kmh, NULL)) AS Speed_End, 
    AVG(Speed_kmh) AS Average_Speed, 
    gu.name, 
    gu.notes, 
    gu.serial 
FROM 
    tmp_k AS k 
    INNER JOIN tmp_max AS m 
     USING(IntervalID) 
    INNER JOIN gps_unit AS gu 
     USING(idgps_unit) 
    INNER JOIN user AS u 
    ON (gu.idcustomer = u.idcustomer) 
WHERE 
    (k.IntervalCheck = 1) 
    AND (u.iduser = 14) 
GROUP BY 
    k.IntervalID, 
    k.idgps_unit; 

DROP TEMPORARY TABLE tmp_k; 
DROP TEMPORARY TABLE tmp_max; 
0

由于I/O分页非常长时间。

这就是说,你可以改善与下面的提示你的表现:

  • 在嵌套查询
  • 增加缓冲池的大小,选择尽可能少的数据成为可能。
0

我个人的经验表明,MySQL在处理子查询时相当糟糕。数据库的查询优化器是数据库中非常复杂和美味的一部分,商业数据库供应商付出了很多努力,所以这是恕我直言,难怪MySQL在处理由更疯狂的开发人员发明的疯狂SQL语句时表现得相当差;-)。

在这里看到:http://dev.mysql.com/doc/refman/5.6/en/subquery-restrictions.html

优化是比子查询的连接更加成熟,所以在很多 情况下,使用一个子查询语句可以执行更 如果有效,你把它改写为联合, 。

如果从Oracle状态的东西MySQL官方文档,如“更成熟”,那么你可以放心,它实际上是一个类似于废话(没有双关语意,但我已经有我的问题与MySQL和大多数的用商业数据库完美运行的大型语句,宁可杀死mysql)。

所以任务是:使用连接....