UNION działa powoli, ale oba zapytania są szybkie w osobnych

11

Nie wiem, co jeszcze z tym zrobić. Mam jedną tabelę, która ma kolumny start i stop, i chcę zwrócić jej wyniki połączone przez start i stop i chcę wyraźnego rozróżnienia między nimi. Teraz oba zapytania działają szybko osobno:

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

Więc ten zajmuje 0,063. Ale jeśli połączę to w UNIĘ (nieważne, czy to UNIA WSZYSTKO LUB WYRÓŻNIONA LUB Cokolwiek), zajmuje to tylko około 0,400 sekundy.

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)

Oto WYJAŚNIJ na pojedyncze zapytanie:

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

A oto WYJAŚNIENIE DOŁĄCZENIA:

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                     

Pomoc w tej sprawie byłaby bardzo mile widziana. :)

EDYTOWAĆ:

Otrzymuję niespójny wynik. Jeśli na przykład usunę convert_tz i spróbuję wydostać się ze strefy czasowej poza związek, otrzymam bardzo szybkie wyniki, ale jeśli zmienię nazwę wyniku, automatycznie przejdzie do tego samego zapytania o niskiej wydajności:

SELECT
    *,
    GetCarrierTimezone(carrier_id) timezone
FROM
(

zajmuje to 0.374s

SELECT
    *,
    GetCarrierTimezone(carrier_id)
FROM
(

podczas gdy zajmuje to 0,078 (głównie opóźnienie od db do mojej maszyny) ..

holderjsm
źródło
Najprościej byłoby uruchomić je osobno i połączyć wyniki w aplikacji.
ypercubeᵀᴹ
cześć @ypercube, które przyszło mi do głowy :) ale to takie brzydkie robić i utrzymywać ten kod. Poza tym wciąż muszę sortować wyniki w php.
helderjsm
Miałem na myśli uruchomienie 2 zapytań z poszukiwanym rodzajem. Następnie musisz tylko połączyć w php (bez sortowania).
ypercubeᵀᴹ
1
Sortowanie nie jest liniowe. Wynik zapytania 1 może znajdować się pomiędzy wynikami zapytania 2.
holderjsm
1
Nie sądzę, że @ypercube zakłada, że ​​wyniki się nie pokrywają: „scalenie” jest znacznie tańsze / łatwiejsze niż rodzaj do implementacji w php. Oczywiście rozwiązanie problemu w SQL, jeśli to możliwe, byłoby znacznie lepszym rozwiązaniem :)
Jack mówi, spróbuj wypróbować topanswers.xyz

Odpowiedzi:

1

Spodziewałbym się, że tak się stanie z powodu ORDER BY.

Spróbuj tego w pierwszej części UNII:

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

A to w drugiej części:

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

A następnie zastąpić ORDER BYz

ORDER BY alertFoo

Innymi słowy, usuń potrzebę IF w kolejności przez.

Thomas Kejser
źródło
Cześć Thomas, Przede wszystkim dziękuję za powtórkę. Jak powiedziałem w poprzednim poście, zostało to naprawione jakiś czas temu. Chodzi o to, że potrzebowałem rozróżnienia między ostrzeżeniem 1 i ostrzeżeniem 2. W każdym razie kolejność jest wykonywana na podstawie wyników złączeń, a nie na samym złączeniu. Nie było tak wielu wyników, które uzasadniałyby powolność zapytania.
helderjsm
0

W bardzo podobnym przypadku zauważyłem z listy procesów mysql bardzo złe zachowanie „kopiuj do tabeli tymczasowej” (kopiowanie Co? Nie wiem). Myślę, że mysql skusił „najlepsze podejście” do tworzenia zapytań, ale w tym przypadku zawiodło, więc użycie kodu do „scalenia” wyników 2 zapytań działało dobrze.

realtebo
źródło
Cześć, Realtebo, Dzięki za wkład. To już trochę stare, ale pamiętam, że niekonsekwencja polegała na tym, że niektórzy mysql buforowali niektóre wyniki, a nie inne. Ostatecznie odtworzyłem zapytanie w bardziej wydajny sposób, szczególnie śledząc wartości, których potrzebowałem w osobnej tabeli, dzięki czemu indeksy są bardziej wydajne.
helderjsm
0

Głównym powodem wolniejszego działania union sql jest to, że związek powoduje, że mysqld tworzy wewnętrzną tabelę tymczasową. Tworzy tylko tabelę dla UNION ALL i tabelę z indeksem (aby usunąć duplikaty) dla UNION DISTINCT.

Mam nadzieję że to pomoże.

cześć
źródło