UNION은 느리지 만 두 쿼리는 별도로 빠릅니다.


11

이 일에 대해 다른 조치를 취하지 마십시오. 시작 및 중지 열이있는 하나의 테이블이 있고 시작 및 중지로 결합 된 결과를 반환하고 싶습니다. 두 테이블을 명확하게 구별하고 싶습니다. 이제 두 쿼리 모두 별개로 빠르게 실행됩니다.

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 또는 WHATEVER 상관 없음) 약 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

그리고 여기에 가입에 대한 설명이 있습니다 :

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로 결과를 정렬해야합니다.
helderjsm 2014 년

원하는 정렬로 2 개의 쿼리를 실행해야했습니다. 그런 다음 PHP로만 병합하면됩니다 (정렬 없음).
ypercubeᵀᴹ

1
정렬이 선형이 아닙니다. 쿼리 1의 결과는 쿼리 2의 결과 사이에있을 수 있습니다.
helderjsm

1
@ypercube는 결과가 겹치지 않는다고 가정한다고 생각하지 않습니다. '병합'은 PHP에서 구현하는 것보다 훨씬 저렴합니다. 훨씬 더 나은 솔루션 : 것, 가능하면 물론 SQL의 문제를 해결 중
잭 topanswers.xyz 시도라고

답변:


1

나는 당신이 거기에있는 주문으로 인해 이런 일이 일어날 것으로 기대합니다.

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의 구별이 필요하다는 것입니다. 어쨌든 순서는 조인 자체가 아닌 조인 결과에 따라 수행됩니다. 쿼리 속도 저하를 정당화하기위한 많은 결과가 없었습니다.
helderjsm

0

매우 비슷한 경우, mysql의 프로세스 목록에서 'copy to temp table'의 매우 나쁜 동작을 알았습니다 (무엇을 복사합니까? 모릅니다). mysql은 쿼리에 대한 '최상의 접근 방식'을 유혹했지만이 경우 실패했기 때문에 코드를 사용하여 2 쿼리 결과를 '병합'하는 것이 좋습니다.


안녕하세요 realtebo, 입력 주셔서 감사합니다. 이것은 조금 오래되었지만 불일치를 기억하는 이유는 mysql이 일부 결과를 캐싱하고 다른 결과는 캐싱하지 않았기 때문입니다. 결국 인덱스를 더 효율적으로 만드는 별도의 테이블에서 필요한 값을 추적하여 쿼리를보다 효율적인 방식으로 다시 작성했습니다.
helderjsm

0

통합 SQL이 느리게 실행되는 주된 이유는 통합으로 인해 mysqld가 내부 임시 테이블을 생성하기 때문입니다. UNION ALL에 대한 테이블과 UNION DISTINCT에 대한 인덱스 (중복 제거)가있는 테이블 만 작성합니다.

도움이 되었기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.