我有这个问题:
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类型查找。
指标:
EXPLAIN PLAN:
我尝试过使用FORCE INDEX FOR JOIN尝试将其编入索引日期,但它似乎没有提示。
dwDimDate是一年中有几天的日期维度,因此在这种情况下我认为限制为365天会很快,然后在该日期范围内查找所有票证。在该日期范围内应该只有大约5K票。
非常感谢任何帮助。我不知道如何找出采用什么策略去除" ALL"抬头。我想知道将来如何做到这一点,所以如果你可以帮助我教会钓鱼,那就太好了。
修改 该查询目前需要11秒才能运行,这将是生产中的一个问题。
答案 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;