SQL 查询耗时较长 运行

SQL Query taking a long time to run

我正在尝试 运行 这个 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)

但由于某种原因,return 需要 14.8126 秒才能得到任何结果。

测试时,它 returning 1 行,我不明白为什么它这么慢。我相信这可能与连接有关,但我希望有人能帮助我并指出正确的方向?

如果我遗漏了任何信息,请原谅我的无知。

使用EXPLAIN查看执行计划。

查询已经在为 b 使用反连接模式。

我建议使用相同的反连接模式来代替 NOT IN 检查。 (anti-join 的大驼峰好像把我们的脑袋都绕过去了;一旦我们理解了这个模式,我们应该就可以使用它了。

像这样:

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 以获得等效计划。 (与直觉相反,反连接模式有时会在 EXPLAIN 输出的 Extra 列中显示 "not exists",而 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'
       )

除了其他答案,您还需要这些索引:

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

可能有助于结合LEFT JOINNOT IN:

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