MySQL索引和解释计划

时间:2017-11-22 21:28:08

标签: mysql sql-execution-plan explain

我有这个问题:

SELECT * FROM dwDimDate d 
LEFT JOIN tickets t FORCE INDEX FOR JOIN (idx_tickets_id_and_date) ON 
DATE_FORMAT(t.ticket_date, '%Y%m%d') = d.date_key 
LEFT JOIN sales s ON s.ticket_id = t.ticket_id
WHERE d.date_key BETWEEN 20130101 AND 20131231
GROUP BY d.date_key 

我正在寻求帮助来优化它。我一直在阅读理解解释计划的所有内容,并根据其进行优化,但我似乎无法阻止MySQL在故障单表格中使用ALL类型查找。

指标:

enter image description here

EXPLAIN PLAN:

enter image description here

我尝试过使用FORCE INDEX FOR JOIN尝试将其编入索引日期,但它似乎没有提示。

dwDimDate是一年中有几天的日期维度,因此在这种情况下我认为限制为365天会很快,然后在该日期范围内查找所有票证。在该日期范围内应该只有大约5K票。

非常感谢任何帮助。我不知道如何找出采用什么策略去除" ALL"抬头。我想知道将来如何做到这一点,所以如果你可以帮助我教会钓鱼,那就太好了。

修改 该查询目前需要11秒才能运行,这将是生产中的一个问题。

3 个答案:

答案 0 :(得分:3)

ON DATE_FORMAT(t.ticket_date, '%Y%m%d') = d.date_key 

当您在t.ticket_date列上使用函数时,这将永远不会使用索引。

FORCE INDEX并没有神奇地将非sargable表达式变成sargable表达式。它只是暗示优化器假设表扫描是无限昂贵的。因此优化器会说,嗯,这很糟糕,因为这个连接表达式需要进行表扫描。"

一种解决方案是以通用格式存储t.ticket_date和d.date_key。使用DATE列或“YYYYmmdd”'两个字符串。

第二种可能的解决方案:根据t.ticket_date创建一个虚拟列并索引虚拟列。

ALTER TABLE tickets 
  ADD COLUMN ticket_date_yyyymmdd AS (DATE_FORMAT(ticket_date, '%Y%m%d'),
  ADD INDEX (ticket_date_yyyymmdd);

答案 1 :(得分:2)

问题是你正在尝试加入列的函数,而不是列本身的值。因此,它无法在ticket_date上使用您的索引来执行加入。

理想情况下,您应该确保ticket_date采用与date_key兼容的格式,这样您就可以进行简单的比较或范围查询。如果这对您来说绝对不是一个选项,并且您在InnoDB上使用相对较新版本的MySQL(5.7.8 +),则可以创建一个虚拟列,并在其上创建effectively create a functional index

答案 2 :(得分:0)

这可能更接近有效查询,并且应该更快,至少在MySQL 5.6或更新版本上:

SELECT  *
    FROM  dwDimDate AS d
    LEFT JOIN  
        ( SELECT  MIN(ticket_id) AS one_tic_id,
                  COUNT(*) AS num_tickets,
                  DATE(ticket_date) AS date_key
            FROM  tickets t
            LEFT JOIN  sales s
               ON s.ticket_id = t.ticket_id
        ) AS ts USING (date_key)
    WHERE  d.date_key >= '2013-01-01'
      AND  d.date_key <  '2013-01-01' + INTERVAL 1 MONTH
    GROUP BY  d.date_key;