UNION работает медленно, но оба запроса выполняются быстро в разных

11

Не знаю, что еще делать с этим. У меня есть одна таблица, в которой есть столбцы start и stop, и я хочу вернуть ее результаты, объединенные как start, так и stop, и я хочу провести четкое различие между ними. Теперь оба запроса выполняются быстро раздельно:

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 OR DISTINCT OR 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)

Вот ОБЪЯСНЕНИЕ по одному запросу:

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

И вот ОБЪЯСНЕНИЕ для СОЕДИНЕНИЯ:

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 (в основном, отставание от БД до моей машины) ..

helderjsm
источник
Простейшим будет запустить их отдельно и объединить результаты в приложении.
ypercubeᵀᴹ
Привет @ypercube, это пришло мне в голову :), но это так ужасно, чтобы сделать это и поддерживать этот код. Кроме того, мне все еще нужно отсортировать результаты в php.
helderjsm
Я имел в виду запустить 2 запроса с нужной сортировкой. Тогда вам нужно только слить в php (без сортировки).
ypercubeᵀᴹ
1
Сортировка не линейная. Результат запроса 1 может находиться между результатами запроса 2.
helderjsm
1
Я не думаю, что @ypercube предполагает, что результаты не пересекаются: «слияние» намного дешевле / проще, чем сортировка в php. Конечно, решение проблемы в SQL, если это возможно, будет гораздо лучшим решением :)
Джек говорит, что попробуйте topanswers.xyz

Ответы:

1

Я ожидаю, что это произойдет из-за того, что у вас там ЗАКАЗ.

Попробуйте это в первой части UNION:

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

И это во второй части:

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. В любом случае заказ выполняется по результату объединений, а не по самому объединению. Было не так много результатов, чтобы оправдать медлительность запроса.
helderjsm
0

В очень похожем случае я заметил из списка процессов mysql очень плохое поведение «копировать в временную таблицу» (копирование «Что? Я не знаю»). Я думаю, что mysql соблазнил «лучший подход» для запросов, но в этом случае не получилось, поэтому использование кода для «слияния» результатов двух запросов работало нормально.

realtebo
источник
Привет realtebo, спасибо за вклад. Сейчас это немного устарело, но я помню, что несоответствие было связано с тем, что в некоторых случаях mysql кэшировал одни результаты, а другие - нет. В конце концов, я воссоздала запрос более эффективным способом, специально отслеживая нужные значения в отдельной таблице, делая индексы более эффективными.
helderjsm
0

Основная причина медленного выполнения объединения sql заключается в том, что объединение заставляет mysqld создать внутреннюю временную таблицу. Он создает только таблицу для UNION ALL и таблицу с индексом (для удаления дубликатов) для UNION DISTINCT.

Надеюсь это поможет.

hiyall
источник