UNION es lento pero ambas consultas son rápidas por separado

11

No sé qué más hacer con este. Tengo una tabla que tiene una columna de inicio y una de detención y quiero devolver los resultados unidos por inicio y por detención y quiero una distinción clara entre las dos. Ahora ambas consultas se ejecutan rápidamente por separado:

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

Entonces este toma 0.063. Pero si lo combino en una UNION (no importa si es UNION ALL O DISTINCT O LO QUE SEA), solo toma alrededor de 0.400 segundos.

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)

Aquí está EXPLICAR en una sola consulta:

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

Y aquí está la EXPLICACIÓN de la UNIÓN:

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                     

Ayuda en este sería muy apreciada. :)

EDITAR:

Estoy obteniendo un resultado inconsistente. Si elimino convert_tz, por ejemplo, e intento obtener la zona horaria fuera de la unión, obtengo resultados muy rápidos, pero si cambio el nombre del resultado, automáticamente se reduce a la misma consulta de bajo rendimiento:

SELECT
    *,
    GetCarrierTimezone(carrier_id) timezone
FROM
(

esto toma 0.374s

SELECT
    *,
    GetCarrierTimezone(carrier_id)
FROM
(

mientras que esto toma 0.078 (principalmente el retraso de la base de datos a mi máquina) ...

helderjsm
fuente
Lo más sencillo sería ejecutarlos por separado y combinar los resultados en la aplicación.
ypercubeᵀᴹ
hola @ypercube, eso me pasó por la mente :) pero es muy feo hacer eso y mantener ese código. Además, todavía tengo que ordenar los resultados en PHP.
helderjsm
Me refería a ejecutar las 2 consultas con el tipo deseado. Entonces solo necesita fusionarse en php (sin ordenar).
ypercubeᵀᴹ
1
La clasificación no es lineal. El resultado de la consulta 1 puede estar entre los resultados de la consulta 2.
helderjsm
1
No creo que @ypercube asuma que los resultados no se superponen: una 'fusión' es mucho más barata / fácil que implementarla en php. Por supuesto, solucionar el problema en el SQL si es posible sería una solución mucho mejor :)
Jack dice que intente topanswers.xyz

Respuestas:

1

Esperaría que esto suceda debido al ORDEN POR que tienes allí.

Prueba esto en la primera parte de UNION:

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

Y esto en la segunda parte:

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

Y luego reemplazar el ORDER BYcon

ORDER BY alertFoo

En otras palabras, elimine la necesidad del IF en el orden de.

Thomas Kejser
fuente
Hola Thomas, en primer lugar, gracias por tu repetición. Como dije en una publicación anterior, esto se solucionó hace algún tiempo. El caso es que necesitaba la distinción entre la alerta 1 y la alerta 2. En cualquier caso, el orden se realiza en el resultado de las uniones y no en la unión misma. No hubo tantos resultados para justificar la lentitud de la consulta.
helderjsm
0

En un caso muy similar, noté en la lista de procesos de mysql el muy mal comportamiento de 'copiar a la tabla temporal' (copiando ¿Qué? No lo sé). Creo que mysql tentó un 'mejor enfoque' para las consultas, pero en este caso falló, por lo que usar el código para 'fusionar' los resultados de 2 consultas funcionó bien.

Realtebo
fuente
Hola realtebo, gracias por el aporte. Esto es un poco viejo ahora, pero por lo que recuerdo la inconsistencia fue porque mysql estaba almacenando en caché algunos resultados y no otros. Eventualmente recreé la consulta de una manera más eficiente, especialmente haciendo un seguimiento de los valores que necesitaba en una tabla separada para que los índices fueran más eficientes.
helderjsm
0

La razón principal para que la unión sql se ejecute más lentamente es que una unión hace que mysqld cree una tabla temporal interna. Crea solo una tabla para UNION ALL y una tabla con un índice (para eliminar duplicados) para UNION DISTINCT.

Espero que esto ayude.

hola a todos
fuente