UNION повільний, але обидва запити швидко проходять окремо


11

Не знаю, що ще робити з цим. У мене є одна таблиця, в якій є стовпчики початку та зупинки, і я хочу повернути її результати, приєднані як до запуску, так і до зупинки, і я хочу чітко розрізняти їх. Тепер обидва запити швидко виконуються окремо:

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. Але якщо я комбіную його в союзі (неважливо, чи це СОЮЗ ВСІ, АБО РАЗЛИЧНО ІЛИ ЩО), це займе приблизно 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                     

Допомога в цьому буде дуже вдячна. :)

Редагувати:

Я отримую непослідовний результат. Якщо я видаляю, наприклад, conver_tz і намагаюся отримати часовий пояс поза союзом, я отримую дуже швидкі результати, але якщо я перейменую результат, він автоматично переходить на той же запит, що не відповідає результатам:

SELECT
    *,
    GetCarrierTimezone(carrier_id) timezone
FROM
(

це займає 0,374s

SELECT
    *,
    GetCarrierTimezone(carrier_id)
FROM
(

в той час як це займає 0,078 (переважно відставання від db до моєї машини) ..


Найпростіше було б запустити їх окремо і об'єднати результати в додатку.
ypercubeᵀᴹ

привіт @ypercube, що перейшло в голову :), але це так некрасиво, щоб це зробити і підтримувати цей код. Крім того, мені ще доведеться сортувати результати в php.
holderjsm

Я мав на увазі запустити два запити із потрібним сортом. Тоді вам потрібно лише об'єднатись у php (без сортування).
ypercubeᵀᴹ

1
Сортування не є лінійним. Результат запиту 1 може бути між результатами запиту 2.
holderjsm

1
Я не думаю, що @ypercube припускає, що результати не збігаються: "злиття" набагато дешевше / простіше, ніж сорт для впровадження в php. Звичайно, вирішення проблеми в SQL, якщо можливо, було б набагато кращим рішенням :)
Джек каже спробувати topanswers.xyz

Відповіді:


1

Я б очікував, що це станеться через ЗАМОВЛЕННЯ, яке ви маєте там.

Спробуйте це у першій частині Спілки:

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

Іншими словами, усуньте необхідність в ІФ у порядку на.


Привіт Томасе, перш за все дякую за повтор. Як я вже говорив у попередньому дописі, це було виправлено десь тому. Річ у тому, що мені потрібно було розрізняти попередження 1 і попередження 2. У будь-якому випадку замовлення робиться за результатами приєднання, а не за самим з'єднанням. Не так багато було результатів, щоб виправдати повільність запиту.
holderjsm

0

У дуже подібному випадку я помітив із списку процесів mysql дуже погану поведінку "копіювати в темп-таблицю" (копіювання Що? Я не знаю). Я думаю, що mysql спокусив «найкращий підхід» для запитів, але в цьому випадку не вдалося, тому використання коду для «злиття» результатів 2-запиту спрацювало чудово.


Привіт realtebo, Дякую за вклад Зараз це трохи старе, але, як я пам’ятаю, непослідовність була через те, що деякі, як mysql кешували одні результати, а не інші. Зрештою, я відтворив запит більш ефективним способом, спеціально відстежуючи потрібні мені значення в окремій таблиці, роблячи показники більш ефективними.
holderjsm

0

Основна причина того, що союз sql працює повільніше, полягає в тому, що об'єднання викликає mysqld для створення внутрішньої тимчасової таблиці. Він створює просто таблицю для UNION ALL і таблицю з індексом (для видалення дублікатів) для UNION DISTINCT.

Сподіваюсь, це допомагає.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.