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

現在、783,723 レコードに 3 分かかります。適切なインデックスが役立つと思います。いくつかの試行錯誤の後、私はそれを理解できません。ご協力いただけると思われる場合、追加情報が必要な場合は、喜んでご提供いたします。

説明 説明

結果 結果

4

3 に答える 3

2

インデックスを追加すると多くの場合に役立ちますが、別のサブクエリを結合するサブクエリがあり、現在のテーブルにインデックスがなくても高速化できません。ここでインデックスを使用できる唯一の方法は、一時テーブルを作成することです。

したがって、Markus が指摘したように、クエリをいくつかの小さなクエリに分割して、結果を一時テーブルに格納する必要があります。それらにインデックスを追加して、うまくいけばクエリを高速化することができます。大きなクエリをいくつかの小さなクエリに分割することのもう 1 つの良い点は、どの部分が遅いのかをより適切にプロファイルして修正できることです。

また、1 つのサブクエリを 2 回使用しましたが、結果がキャッシュされなかったため、パフォーマンスが低下しています。

これを行う方法の例を次に示します。

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;
于 2013-03-18T22:14:38.393 に答える
0

ネストされたクエリのバイト単位のサイズがバッファ プールのサイズを超える場合 (チェックinnodb_buffer_pool_size)、I/O ページングのためにクエリに非常に長い時間がかかります。

ただし、次のヒントを使用してパフォーマンスを向上させることができます。

  • ネストされたクエリでできるだけ少ないデータを選択する
  • バッファー プールのサイズを増やします。
于 2013-03-19T00:56:33.683 に答える
0

私の個人的な経験によると、MySQL はサブクエリの処理がかなり苦手です。データベースのクエリ オプティマイザーは、データベースの非常に複雑で優れた部分であり、商用データベース ベンダーはそれに多大な労力を費やしています。 ;-)。

ここを参照してください: http://dev.mysql.com/doc/refman/5.6/en/subquery-restrictions.html :

オプティマイザーは、サブクエリよりもジョインの方が成熟しているため、多くの場合、サブクエリを使用するステートメントは、ジョインとして書き直した方が効率的に実行できます。

Oracle の公式の mysql ドキュメントが「より成熟した」と述べている場合、実際にはがらくたに似たものであると安心できます (しゃれは意図していませんが、MySQL と問題なく実行されるより大きなステートメントのほとんどに問題がありました商用データベースでは、むしろ mysql を強制終了します)。

したがって、タスクは次のとおりです。JOINを使用して書き直します....

于 2013-03-20T18:32:03.473 に答える