Menambahkan indeks membantu dalam banyak kasus, tetapi Anda memiliki subquery yang bergabung dengan subquery lain, tidak ada indeks di tabel Anda saat ini yang dapat membantu Anda mempercepat. Satu-satunya cara Anda dapat menggunakan indeks di sini adalah dengan membuat tabel sementara.
Jadi seperti yang ditunjukkan Markus, Anda perlu memecah kueri Anda menjadi beberapa kueri yang lebih kecil yang menyimpan hasilnya di tabel sementara. Daripada Anda dapat menambahkan indeks ke dalamnya dan semoga mempercepat kueri Anda. Hal baik lainnya tentang memecah kueri besar menjadi beberapa kueri yang lebih kecil adalah Anda dapat membuat profil dengan lebih baik bagian mana yang lebih lambat dan memperbaikinya.
Anda juga telah menggunakan satu subquery dua kali yang buruk untuk kinerja karena hasilnya tidak di-cache.
Berikut adalah contoh bagaimana Anda dapat melakukannya:
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;