我的查询会生成一些关于超速,上次和平均速度的报告。 这是我的问题:
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
目前783,723条记录需要3分钟。我认为适当的索引可能会有所帮助;虽然经过一些试验和错误,我无法弄明白。如果您认为自己可以提供帮助,并需要一些额外的信息 - 我很乐意为您提供帮助。
解释
结果
答案 0 :(得分:2)
在许多情况下添加索引会有所帮助,但是您有一个子查询加入另一个子查询,当前表上没有索引可以帮助您加快速度。您可以在此处使用索引的唯一方法是创建临时表。
因此,Markus指出,您需要将查询分解为几个较小的查询,这些查询将结果存储在临时表中。您可以为它们添加索引,并希望加快查询速度。将大查询分成几个较小的查询的另一个好处是你可以更好地分析哪个部分是较慢的部分并修复它。
您还使用了两次子查询,这对性能有害,因为结果没有缓存。
以下是如何执行此操作的示例:
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;
答案 1 :(得分:0)
如果嵌套查询的大小(以字节为单位)超过缓冲池的大小(检查innodb_buffer_pool_size
),则由于i / o分页,查询将花费很长时间。
那说你可以通过以下提示改善你的表现:
答案 2 :(得分:0)
我的个人经验表明MySQL在处理子查询方面相当糟糕。数据库的查询优化器是数据库的一个非常复杂和美味的部分,商业数据库供应商付出了很多努力,所以恕我直言,毫无疑问MySQL在处理由更疯狂的开发人员发明的疯狂SQL语句时表现相当差; - 。)
见这里:http://dev.mysql.com/doc/refman/5.6/en/subquery-restrictions.html:
对于连接而言,优化器比子查询更成熟,因此在许多情况下 使用子查询的语句可以执行得更多 如果你把它重写成一个连接,就会有效率。
如果来自Oracle的官方mysql文档声明类似“更成熟”的东西,那么你可以放心它实际上类似于垃圾(没有双关语意图,但我已经遇到了MySQL的问题和大多数较大的陈述用商业数据库完美运行,宁愿杀掉mysql)。
所以任务是:使用JOIN重写它....