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) ..


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.


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.


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.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.