UNION é lento, mas as duas consultas são rápidas em separado

11

Não sei mais o que fazer sobre este. Eu tenho uma tabela que tem colunas de início e de parada e quero retornar os resultados dela unidos por início e por parada e quero uma distinção clara entre as duas. Agora, as duas consultas são executadas rapidamente separadamente:

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

Então este leva 0,063. Mas se eu combiná-lo em um UNION (não importa se é UNION ALL OU DISTINCT OU WHATEVER), leva apenas 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)

Aqui está EXPLAIN na consulta única:

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

E aqui está o EXPLAIN para o 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                     

Ajuda sobre este seria muito apreciada. :)

EDITAR:

Estou obtendo resultado inconsistente. Se eu remover o convert_tz, por exemplo, e tentar obter o fuso horário fora da união, obterei resultados muito rápidos, mas, se eu renomear o resultado, ele desce automaticamente para a mesma consulta de baixo desempenho:

SELECT
    *,
    GetCarrierTimezone(carrier_id) timezone
FROM
(

isso leva 0,374s

SELECT
    *,
    GetCarrierTimezone(carrier_id)
FROM
(

enquanto isso leva 0,078 (principalmente o atraso do banco de dados para minha máquina) ..

helderjsm
fonte
O mais simples seria executá-los separadamente e combinar os resultados no aplicativo.
ypercubeᵀᴹ
oi @ypercube, que passou pela minha cabeça :) mas é tão feio fazer isso e manter esse código. Além disso, ainda tenho que classificar os resultados em php.
helderjsm
Eu quis dizer executar as 2 consultas com o tipo desejado. Então você só precisa mesclar em php (sem classificação).
precisa saber é o seguinte
11
A classificação não é linear. O resultado da consulta 1 pode estar entre os resultados de consulta 2.
helderjsm
11
Eu não acho que @ypercube está assumindo que os resultados não se sobrepõem: uma 'mesclagem' é muito mais barata / fácil do que uma espécie para implementar em php. De fixar claro que o problema no SQL se possível seria uma solução muito melhor :)
Jack diz tentar topanswers.xyz

Respostas:

1

Eu esperaria que isso acontecesse devido à ORDEM POR que você tem lá.

Tente isso na primeira parte da UNIÃO:

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

E isso na segunda parte:

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

E depois substitua o ORDER BYpor

ORDER BY alertFoo

Em outras palavras, remova a necessidade do FI na ordem de.

Thomas Kejser
fonte
Olá Thomas, Antes de tudo, obrigado pela sua repetição. Como eu disse em um post anterior, isso foi corrigido há algum tempo. O problema é que eu precisava da distinção entre o alerta 1 e o alerta 2. De qualquer forma, a ordem é feita no resultado das junções e não na junção em si. Não havia muitos resultados para justificar a lentidão da consulta.
helderjsm
0

Em um caso muito parecido, notei na lista de processos do mysql o péssimo comportamento de 'copiar para tabela temporária' (copiando o quê? Não sei). Eu acho que o mysql tentou uma 'melhor abordagem' para consulta, mas nesse caso falhou, então o uso do código para 'mesclar' os resultados de 2 consultas funcionou bem.

realtebo
fonte
Oi realtebo, Obrigado pela entrada. Isso é um pouco antigo agora, mas pelo que me lembro da inconsistência, foi porque alguns como o mysql estava armazenando alguns resultados e outros não. Eventualmente, recriei a consulta de uma maneira mais eficiente, especialmente acompanhando os valores necessários em uma tabela separada, tornando os índices mais eficientes.
precisa saber é o seguinte
0

A principal razão para o union sql rodar mais devagar é que uma união faz com que o mysqld crie uma tabela temporária interna. Ele cria apenas uma tabela para um UNION ALL e uma tabela com um índice (para remover duplicatas) para um UNION DISTINCT.

Espero que isto ajude.

hiyall
fonte