UNION è lento ma entrambe le query sono veloci in modo separato


11

Non so cos'altro fare su questo. Ho una tabella che ha colonne di inizio e di fine e voglio restituire i risultati di essa uniti sia per inizio che per sosta e voglio una chiara distinzione tra i due. Ora entrambe le query vengono eseguite rapidamente separatamente:

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

Quindi questo richiede 0,063. Ma se lo combino in un UNION (non importa se è UNION ALL O DISTINCT O WHATEVER) ci vogliono solo circa 0,400 secondi.

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)

Ecco EXPLAIN su una singola query:

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

Ed ecco EXPLAIN per 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                     

L'aiuto su questo sarebbe molto apprezzato. :)

MODIFICARE:

Sto ottenendo un risultato incoerente. Se rimuovo convert_tz per esempio e provo a ottenere il fuso orario al di fuori del sindacato ottengo risultati molto veloci, ma se rinomino il risultato passa automaticamente alla stessa query con prestazioni inferiori:

SELECT
    *,
    GetCarrierTimezone(carrier_id) timezone
FROM
(

questo richiede 0,374 secondi

SELECT
    *,
    GetCarrierTimezone(carrier_id)
FROM
(

mentre questo richiede 0,078 (principalmente il ritardo dal db alla mia macchina) ..


Il più semplice sarebbe eseguirli separatamente e combinare i risultati nell'applicazione.
ypercubeᵀᴹ

ciao @ypercube, che mi è passato per la testa :) ma è così brutto farlo e mantenere quel codice. Inoltre devo ancora ordinare i risultati in php.
helderjsm,

Intendevo eseguire le 2 query con l'ordinamento desiderato. Quindi devi solo unire in php (nessun ordinamento).
ypercubeᵀᴹ

1
L'ordinamento non è lineare. Il risultato della query 1 può trovarsi tra i risultati della query 2.
helderjsm

1
Non credo che @ypercube stia assumendo che i risultati non si sovrappongano: una 'fusione' è molto più economica / più semplice di una sorta da implementare in php. Ovviamente risolvere il problema
nell'SQL

Risposte:


1

Mi aspetto che ciò accada a causa dell'ORDINE BY che hai lì dentro.

Prova questo nella prima parte dell'UNIONE:

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

E questo nella seconda parte:

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

E quindi sostituire il ORDER BYcon

ORDER BY alertFoo

In altre parole, rimuovere la necessità dell'IF nell'ordine di.


Ciao Thomas, prima di tutto grazie per il tuo replay. Come ho detto in un post precedente, questo problema è stato risolto qualche tempo fa. Il fatto è che avevo bisogno della distinzione tra l'avviso 1 e l'avviso 2. In ogni caso l'ordine viene eseguito sul risultato dei join e non sul join stesso. Non c'erano così tanti risultati per giustificare la lentezza della query.
helderjsm,

0

In un caso molto simile, ho notato dalla lista dei processi di mysql il pessimo comportamento di "copia nella tabella temporanea" (copiare cosa? Non lo so). Penso che mysql abbia tentato un "approccio migliore" per le query, ma in questo caso non è riuscito, quindi l'uso del codice per "unire" i risultati di 2 query ha funzionato bene.


Ciao realtebo, grazie per l'input. Questo è un po 'vecchio ora, ma per quello che ricordo l'incoerenza era perché alcuni come mysql stava memorizzando nella cache alcuni risultati e non altri. Alla fine ho ricreato la query in un modo più efficiente soprattutto tenendo traccia dei valori di cui avevo bisogno in una tabella separata, rendendo gli indici più efficienti.
helderjsm,

0

Il motivo principale per cui l'unione sql funziona più lentamente è che un'unione fa sì che mysqld crei una tabella temporanea interna. Crea solo una tabella per UNION ALL e una tabella con un indice (per rimuovere i duplicati) per UNION DISTINCT.

Spero che sia di aiuto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.