SQL-запрос занимает много времени для запуска - PullRequest
0 голосов
/ 09 мая 2018

Я пытаюсь запустить этот SQL-запрос:

SELECT 
        a.ticketnumber 
    FROM 
        ticket_updates a 
    LEFT JOIN 
        ticket_updates b 
    ON 
        b.ticketnumber = a.sequence AND b.type = 'reminder_complete' 
    WHERE 
        b.ticketnumber IS NULL AND 
        (a.type = 'reminder' OR a.type = 'reminder_high') AND 
        (a.for_agent = '' OR a.for_agent = '2') AND 
        a.notes <= '2018-05-10 23:00:00' AND 
        a.ticketnumber NOT IN (
    SELECT ticketnumber 
                                FROM ticket_updates 
                                WHERE 
                                type = 'reminder_complete' AND 
                                ticketnumber = a.ticketnumber)

Но по какой-то причине для возврата результатов требуется 14,8126 секунд.

При тестировании возвращается 1 строка, и я не могу понять, почему он такой медленный. Я полагаю, что это, возможно, связано с объединениями, но я надеюсь, что кто-то может помочь и указать мне правильное направление, пожалуйста?

Я прошу прощения, если я пропустил какую-либо информацию, поэтому, пожалуйста, извините мое невежество.

Ответы [ 2 ]

0 голосов
/ 21 мая 2018

Помимо других ответов, вам нужны следующие индексы:

a:  (type, for_agent, notes, ticket_number)
a:  (for_agent, type, notes, ticket_number)
ticket_updates:  (type, ticket_number)

Это может помочь объединить LEFT JOIN и NOT IN:

AND NOT EXISTS 
   ( SELECT 1 
       FROM ticket_updates b
      WHERE b.ticketnumber IN ( a.sequence, a.ticketnumber )
        AND b.type = 'reminder_complete' )
   )
0 голосов
/ 10 мая 2018

Используйте EXPLAIN, чтобы увидеть план выполнения.

В запросе уже используется шаблон предотвращения объединения для b.

Я рекомендую использовать тот же шаблон анти-объединения вместо проверки NOT IN. (Большой горб с анти-объединением, кажется, обволакивает наш мозг; как только мы поймем схему, мы сможем ее использовать.

Примерно так:

SELECT a.ticketnumber
  FROM ticket_updates a
    -- anti-join
  LEFT
  JOIN ticket_updates b
    ON b.ticketnumber = a.sequence
   AND b.type         = 'reminder_complete'
    -- anti-join
  LEFT
  JOIN ticket_updates c
    ON c.ticketnumber = a.ticketnumber
   AND c.type         = 'reminder_complete'
    --
 WHERE c.ticketnumber IS NULL
   AND b.ticketnumber IS NULL
    --
   AND a.type      IN ('reminder','reminder_high')
   AND a.for_agent IN ('','2')
   AND a.notes     <= '2018-05-10 23:00:00'

Что касается производительности, нам нужно обеспечить наличие подходящих индексов.

Учитывая, что NOT IN (correlated subquery) вносит вклад во время выполнения, замена его на анти-объединение повышает вероятность того, что MySQL будет использовать подходящий индекс, если он доступен. (С точки зрения производительности, эти повторные выполнения коррелированного подзапроса съедят наш обед и нашу коробку с обедом, если мы не будем осторожны.)

Опять же, используйте EXPLAIN, чтобы увидеть план выполнения.


Шаблон против объединения можно заменить на NOT EXISTS, чтобы получить эквивалентный план. (Интуитивно счётчик, шаблон анти-объединения иногда показывает «не существует» в столбце «Extra» выходных данных «EXPLAIN», а NOT NOT EXISTS - нет.)

Я ожидаю, что это даст план выполнения, который почти эквивалентен:

SELECT a.ticketnumber
  FROM ticket_updates a
 WHERE a.type      IN ('reminder','reminder_high')
   AND a.for_agent IN ('','2')
   AND a.notes     <= '2018-05-10 23:00:00'

   AND NOT EXISTS 
       ( SELECT 1 
           FROM ticket_updates b
          WHERE b.ticketnumber = a.sequence
            AND b.type         = 'reminder_complete'
       )

   AND NOT EXISTS 
       ( SELECT 1 
           FROM ticket_updates c
          WHERE c.ticketnumber = a.ticketnumber
            AND c.type         = 'reminder_complete'
       )
...