Mysql
 sql >> Datenbank >  >> RDS >> Mysql

Abfrageoptimierung – dauert zu lange und stoppt den Server

Das Hinzufügen eines Index hilft in vielen Fällen, aber Sie haben eine Unterabfrage, die sich einer anderen Unterabfrage anschließt, kein Index in Ihrer aktuellen Tabelle kann Ihnen helfen, schneller zu werden. Die einzige Möglichkeit, hier Indizes zu verwenden, besteht darin, eine temporäre Tabelle zu erstellen.

Wie Markus darauf hingewiesen hat, müssen Sie Ihre Abfrage in ein paar kleinere aufteilen, die ihre Ergebnisse in einer temporären Tabelle speichern. Dann können Sie ihnen Indizes hinzufügen und hoffentlich Ihre Abfrage beschleunigen. Eine weitere gute Sache beim Aufteilen großer Abfragen in mehrere kleinere ist, dass Sie besser erkennen können, welcher Teil der langsamere ist, und ihn beheben können.

Sie haben auch eine Unterabfrage zweimal verwendet, was sich negativ auf die Leistung auswirkt, da das Ergebnis nicht zwischengespeichert wurde.

Hier ist ein Beispiel, wie Sie dies tun könnten:

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;