UNION ist langsam, aber beide Abfragen sind getrennt getrennt

11

Keine Ahnung, was ich sonst noch tun soll. Ich habe eine Tabelle mit einer Start- und einer Stoppspalte und möchte die Ergebnisse zurückgeben, die sowohl durch Start als auch durch Stopp verbunden sind, und ich möchte eine klare Unterscheidung zwischen den beiden. Jetzt werden beide Abfragen getrennt schnell ausgeführt:

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

Also dauert dieser 0,063. Aber wenn ich es in einer UNION kombiniere (egal ob es UNION ALL ODER DISTINCT ODER WAS auch immer ist), dauert es nur ungefähr 0,400 Sekunden.

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)

Hier ist EXPLAIN für eine einzelne Abfrage:

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

Und hier ist der EXPLAIN für den JOIN:

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                     

Hilfe in diesem Fall wäre sehr dankbar. :) :)

BEARBEITEN:

Ich bekomme ein inkonsistentes Ergebnis. Wenn ich zum Beispiel convert_tz entferne und versuche, die Zeitzone außerhalb der Union zu ermitteln, erhalte ich sehr schnelle Ergebnisse. Wenn ich das Ergebnis jedoch umbenenne, wird automatisch dieselbe Underperformante-Abfrage ausgeführt:

SELECT
    *,
    GetCarrierTimezone(carrier_id) timezone
FROM
(

Dies dauert 0,374 s

SELECT
    *,
    GetCarrierTimezone(carrier_id)
FROM
(

während dies 0,078 dauert (meistens die Verzögerung von der Datenbank zu meiner Maschine) ..

helderjsm
quelle
Am einfachsten wäre es, sie separat auszuführen und die Ergebnisse in der Anwendung zu kombinieren.
Ypercubeᵀᴹ
hi @ypercube, das ist mir in den Sinn gekommen :) aber es ist so hässlich, das zu tun und diesen Code zu pflegen. Außerdem muss ich die Ergebnisse noch in PHP sortieren.
Helderjsm
Ich meinte, die 2 Abfragen mit der gewünschten Sortierung ausführen. Dann müssen Sie nur noch in PHP zusammenführen (keine Sortierung).
Ypercubeᵀᴹ
1
Die Sortierung ist nicht linear. Das Ergebnis von Abfrage 1 kann zwischen den Ergebnissen von Abfrage 2 liegen.
helderjsm
1
Ich glaube nicht, dass @ypercube davon ausgeht, dass sich die Ergebnisse nicht überschneiden: Eine Zusammenführung ist viel billiger / einfacher als eine in PHP zu implementierende Sortierung. Natürlich wäre es eine weitaus bessere Lösung, das Problem in SQL zu beheben, wenn möglich :)
Jack sagt, versuchen Sie es mit topanswers.xyz am

Antworten:

1

Ich würde erwarten, dass dies aufgrund der BESTELLUNG VON passiert, die Sie dort haben.

Versuchen Sie dies im ersten Teil der UNION:

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

Und das im zweiten Teil:

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

Und dann ersetzen Sie die ORDER BYdurch

ORDER BY alertFoo

Mit anderen Worten, entfernen Sie die Notwendigkeit für die IF in der Reihenfolge von.

Thomas Kejser
quelle
Hallo Thomas, zunächst einmal vielen Dank für deine Wiederholung. Wie ich bereits in einem früheren Beitrag sagte, wurde dies vor einiger Zeit behoben. Die Sache ist, ich brauchte die Unterscheidung zwischen der Warnung 1 und der Warnung 2. In jedem Fall erfolgt die Bestellung nach dem Ergebnis der Verknüpfungen und nicht nach der Verknüpfung selbst. Es gab nicht allzu viele Ergebnisse, um die Langsamkeit der Abfrage zu rechtfertigen.
helderjsm
0

In einem sehr sehr ähnlichen Fall bemerkte ich aus der Prozessliste von MySQL das sehr schlechte Verhalten von 'In temporäre Tabelle kopieren' (Kopieren von Was? Ich weiß nicht). Ich denke, MySQL hat versucht, einen "besten Ansatz" für die Abfrage zu finden, aber in diesem Fall ist dies fehlgeschlagen. Daher hat die Verwendung von Code zum "Zusammenführen" von 2-Abfrage-Ergebnissen gut funktioniert.

Realtebo
quelle
Hallo Realtebo, Danke für die Eingabe. Das ist jetzt ein bisschen alt, aber ich erinnere mich an die Inkonsistenz, weil einige, wie MySQL einige Ergebnisse zwischengespeichert hat, andere nicht. Schließlich habe ich die Abfrage effizienter neu erstellt, insbesondere indem ich die benötigten Werte in einer separaten Tabelle nachverfolgt habe, um die Indizes effizienter zu gestalten.
helderjsm
0

Der Hauptgrund dafür, dass die Union SQL langsamer läuft, ist, dass eine Union dazu führt, dass mysqld eine interne temporäre Tabelle erstellt. Es wird nur eine Tabelle für eine UNION ALL und eine Tabelle mit einem Index (zum Entfernen von Duplikaten) für eine UNION DISTINCT erstellt.

Hoffe das hilft.

Hallo
quelle