UNION很慢,但是两个查询在单独的情况下都很快


11

邓诺(Dunno)对此还有什么可做的。我有一个具有开始和停止列的表,我想返回通过开始和停止连接的表的结果,我希望两者之间有明显的区别。现在,这两个查询分别快速运行:

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

因此,这需要0.063。但是,如果我将其组合成一个UNION(无论是UNION ALL还是DISTINCT或其他都没关系),则只需要0.400秒。

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)

这是单个查询的解释:

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

这是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                     

在这一方面的帮助将不胜感激。:)

编辑:

我得到不一致的结果。例如,如果我删除convert_tz并尝试使时区超出并集,则会得到非常快速的结果,但是如果我重命名结果,它将自动下降到相同的性能欠佳查询:

SELECT
    *,
    GetCarrierTimezone(carrier_id) timezone
FROM
(

这需要0.374s

SELECT
    *,
    GetCarrierTimezone(carrier_id)
FROM
(

而这需要0.078(大部分是从db到我的机器的延迟)。


最简单的方法是分别运行它们并将结果合并到应用程序中。
ypercubeᵀᴹ

嗨,@ ypercube,这让我很烦恼:),但是这样做并维护代码非常丑陋。此外,我仍然必须在php中对结果进行排序。
Holderjsm 2013年

我的意思是使用所需的排序来运行2个查询。然后,您只需要在php中合并(无需排序)。
ypercubeᵀᴹ

1
排序不是线性的。查询1的结果可以在查询2的结果之间
helderjsm

1
我不认为@ypercube假设结果不会重叠:“合并”比在php中实现的排序便宜/容易得多。当然,如果可能的话,在SQL中解决该问题将是一个更好的解决方案:)
杰克说请尝试topanswers.xyz 2013年

Answers:


1

我希望这种情况会因为您在那里的ORDER BY而发生。

在UNION的第一部分中尝试以下操作:

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

第二部分:

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

然后替换ORDER BY

ORDER BY alertFoo

换句话说,按顺序删除对IF的需要。


嗨,托马斯,首先感谢您的重播。就像我在上一篇文章中所说的那样,它是在某个时间之前修复的。我需要区分警报1和警报2。无论如何,顺序是根据联接的结果而不是联接本身完成的。没有太多的结果可以证明查询的缓慢性。
holderjsm 2014年

0

在非常相似的情况下,我从mysql的进程列表中注意到“复制到临时表”的非常糟糕的行为(复制什么?我不知道)。我认为mysql尝试使用“最佳方法”进行查询,但是在这种情况下失败了,因此使用代码“合并” 2-query结果可以正常工作。


嗨,realtebo,感谢您的输入。现在这已经有点老了,但是我记得不一致的原因是因为某些mysql如何缓存某些结果,而不是其他。最终,我以一种更有效的方式重新创建了查询,特别是通过在一个单独的表中跟踪所需的值来使索引更加有效。
holderjsm

0

工会sql运行较慢的主要原因是,工会导致mysqld创建内部临时表。它仅为UNION ALL创建一个表,并为UNION DISTINCT创建一个具有索引(删除重复项)的表。

希望这可以帮助。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.