UNIONは遅いですが、両方のクエリは別々に高速です


11

ダンノ、これについて他に何をすべきか。開始列と停止列を持つ1つのテーブルがあり、開始と停止の両方で結合した結果を返したいと思います。また、2つのテーブルを明確に区別したいと考えています。これで、両方のクエリが別々に高速に実行されます。

SELECT
            UNIX_TIMESTAMP(CONVERT_TZ(start_dev, '+00:00', GetCarrierTimezone(a0.carrier_id))) AS alertStart,
            NULL AS alertStop,
            c0.name AS carrier_name,
            carrier_image,
            l0.Latitude,
            l0.Longitude
        FROM
            carriers AS c0
                INNER JOIN start_stop AS a0 ON a0.carrier_id = c0.id
                    INNER JOIN pcoarg AS l0 ON a0.startLogId = l0.id
        WHERE
                FIND_IN_SET(a0.carrier_id, '89467,1,64578,222625,45013') > 0
            AND
                start_dev > '2013-03-11 11:46:48'
            AND 
                start_dev = (SELECT MIN(start_dev) FROM start_stop AS a1 WHERE a0.carrier_id = a1.carrier_id AND DATE(a1.start_dev) = DATE(a0.start_dev))
        AND IsNotificationInSchedule(22, start_dev) > 0

これは0.063です。しかし、それをUNIONで組み合わせると(UNION ALL、DISTINCT、WHATEVERのいずれでもかまいません)、約0.400秒しかかかりません。

SELECT * FROM
(
    (
        SELECT
            UNIX_TIMESTAMP(CONVERT_TZ(start_dev, '+00:00', GetCarrierTimezone(a0.carrier_id))) AS alertStart,
            NULL AS alertStop,
            c0.name AS carrier_name,
            carrier_image,
            l0.Latitude,
            l0.Longitude
        FROM
            carriers AS c0
                INNER JOIN start_stop AS a0 ON a0.carrier_id = c0.id
                    INNER JOIN pcoarg AS l0 ON a0.startLogId = l0.id
        WHERE
                FIND_IN_SET(a0.carrier_id, '89467,1,64578,222625,45013') > 0
            AND
                start_dev > '2013-03-11 11:46:48'
            AND 
                start_dev = (SELECT MIN(start_dev) FROM start_stop AS a1 WHERE a0.carrier_id = a1.carrier_id AND DATE(a1.start_dev) = DATE(a0.start_dev))
            AND IsNotificationInSchedule(22, start_dev) > 0
    ) UNION ALL (
        SELECT
            NULL AS alertStart,
            UNIX_TIMESTAMP(CONVERT_TZ(stop_dev, '+00:00', GetCarrierTimezone(a0.carrier_id))) AS alertStop,
            c0.name AS carrier_name,
            carrier_image,
            l0.Latitude,
            l0.Longitude
        FROM
            start_stop AS a0
                INNER JOIN carriers AS c0 ON a0.carrier_id = c0.id
                    INNER JOIN pcoarg AS l0 ON a0.stopLogId = l0.id
        WHERE
                FIND_IN_SET(a0.carrier_id, '89467,1,64578,222625,45013') > 0
            AND
                stop_dev > '2013-03-11 11:46:48'
            AND 
                stop_dev = (SELECT MAX(stop_dev) FROM start_stop AS a1 WHERE a0.carrier_id = a1.carrier_id AND DATE(a1.stop_dev) = DATE(a0.stop_dev))
            AND IsNotificationInSchedule(22, start_dev) > 0
    )
) AS startStops
ORDER BY IF(alertStart IS NULL, alertStop, alertStart)

これが単一クエリのEXPLAINです:

1   PRIMARY c0  ALL PRIMARY             17  Using where
1   PRIMARY a0  ref PRIMARY,startstop_carriers_stopdev_idx,georefidx,startstop_carriers_startdev_idx    startstop_carriers_stopdev_idx  4   test_backoffice.c0.id   72  Using where
1   PRIMARY l0  ref id ASC  id ASC  4   test_backoffice.a0.startLogId   1   Using where
2   DEPENDENT SUBQUERY  a1  ref PRIMARY,startstop_carriers_stopdev_idx,georefidx,startstop_carriers_startdev_idx    startstop_carriers_stopdev_idx  4   test_backoffice.a0.carrier_id   72  Using where; Using index

そして、これがJOINのEXPLAINです:

1   PRIMARY <derived2>  system                  0   const row not found
2   DERIVED c0  ALL PRIMARY             17  Using where
2   DERIVED a0  ref PRIMARY,startstop_carriers_stopdev_idx,georefidx,startstop_carriers_startdev_idx    startstop_carriers_stopdev_idx  4   test_backoffice.c0.id   72  Using where
2   DERIVED l0  ref id ASC  id ASC  4   test_backoffice.a0.startLogId   1   Using where
3   DEPENDENT SUBQUERY  a1  ref PRIMARY,startstop_carriers_stopdev_idx,georefidx,startstop_carriers_startdev_idx    startstop_carriers_stopdev_idx  4   test_backoffice.a0.carrier_id   72  Using where; Using index
4   UNION   c0  ALL PRIMARY             17  Using where
4   UNION   a0  ref PRIMARY,startstop_carriers_stopdev_idx,georefidx,startstop_carriers_startdev_idx    startstop_carriers_stopdev_idx  4   test_backoffice.c0.id   72  Using where
4   UNION   l0  ref id ASC  id ASC  4   test_backoffice.a0.stopLogId    1   Using where
5   DEPENDENT SUBQUERY  a1  ref PRIMARY,startstop_carriers_stopdev_idx,georefidx,startstop_carriers_startdev_idx    startstop_carriers_stopdev_idx  4   test_backoffice.a0.carrier_id   72  Using where; Using index
    UNION RESULT    <union2,4>  ALL                     

これについて助けていただければ幸いです。:)

編集:

一貫性のない結果が出ています。たとえば、convert_tzを削除して、ユニオンの外のタイムゾーンを取得しようとすると、非常に高速な結果が得られますが、結果の名前を変更すると、自動的に同じパフォーマンスの低いクエリになります。

SELECT
    *,
    GetCarrierTimezone(carrier_id) timezone
FROM
(

これには0.374秒かかります

SELECT
    *,
    GetCarrierTimezone(carrier_id)
FROM
(

これには0.078かかります(ほとんどの場合、dbから私のマシンへの遅延)。


最も簡単なのは、それらを個別に実行し、結果をアプリケーションで結合することです。
ypercubeᵀᴹ

こんにちは@ypercube、それは私の心を越えました:)しかし、それを実行してそのコードを維持するのはとても醜いです。その上、私はまだ結果をphpでソートする必要があります。
holderjsm 2013年

必要なソートで2つのクエリを実行することを意味しました。次に、phpでマージするだけです(並べ替えなし)。
ypercubeᵀᴹ

1
並べ替えは線形ではありません。クエリ1の結果は、クエリ2の結果の間であることができる
helderjsm

1
@ypercubeが結果が重複しないことを想定しているとは思いません。「マージ」は、phpで実装するソートよりもはるかに安価/簡単です。もちろん、可能であればSQLで問題を修正することははるかに良い解決策になる:)
ジャックはtopanswers.xyz試し言う

回答:


1

あなたがそこに持っているORDER BYのせいで、これが起こると思います。

UNIONの最初の部分でこれを試してください:

SELECT
            UNIX_TIMESTAMP(CONVERT_TZ(start_dev, '+00:00', GetCarrierTimezone(a0.carrier_id))) AS alertFoo,
            /* NULL AS alertStop, */

そして、これは2番目の部分で:

SELECT
            /* NULL AS alertStart, */
            UNIX_TIMESTAMP(CONVERT_TZ(stop_dev, '+00:00', GetCarrierTimezone(a0.carrier_id))) AS alertFoo,

そして置き換えるORDER BY

ORDER BY alertFoo

言い換えれば、順序でIFの必要性を排除します。


こんにちはトーマス、まず初めにリプレイをありがとう。以前の投稿で言ったように、これはいつか修正されました。重要なのは、アラート1とアラート2の区別が必要なことです。いずれの場合でも、順序は結合自体ではなく、結合の結果に対して行われます。クエリの遅さを正当化する結果はそれほど多くありませんでした。
holderjsm 2014

0

非常によく似たケースで、mysqlのプロセスリストから、「一時テーブルにコピーする」という非常に悪い動作に気づきました(何をコピーしているのかわかりません)。mysqlはクエリに対して「最善のアプローチ」を試みたと思いますが、この場合は失敗したため、コードを使用して2クエリの結果を「マージ」することはうまくいきました。


こんにちはrealtebo、入力をありがとう。これは少し古いですが、覚えているのは、mysqlが一部の結果をキャッシュしていて、他の結果をキャッシュしていないためです。特にインデックスをより効率的にする別のテーブルで必要な値を追跡することにより、クエリをより効率的な方法で最終的に再作成しました。
holderjsm 2013

0

union sqlの実行速度が遅くなる主な理由は、unionによってmysqldが内部一時テーブルを作成するためです。UNION ALLのテーブルと、UNION DISTINCTのインデックス(重複を削除するため)を含むテーブルのみを作成します。

お役に立てれば。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.