MySQL 索引和解释计划
MySQL Index and Explain Plan
我有这个查询:
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 在票证 table 上使用 ALL 类型查找。
索引:
解释计划:
我试过使用 FORCE INDEX FOR JOIN 来尝试让它从日期开始索引,但它似乎没有接受提示。
dwDimDate 是一个包含一年中第几天的日期维度,因此在这种情况下,我认为限制为 365 天会很快,然后找到该日期范围内的所有工单。在该日期范围内应该只有大约 5000 张门票。
如有任何帮助,我们将不胜感激。我不知道如何弄清楚采用什么策略来删除“ALL”查找。想知道以后怎么弄,如果你能帮我“授人以渔”就好了。
编辑
该查询目前需要 11 秒才能完成 运行,这将成为生产环境中的一个问题。
问题是您正在尝试连接列的函数,而不是列本身的值。因此,它无法使用您在 ticket_date
上的索引来执行连接。
理想情况下,您应该确保 ticket_date
的格式与 date_key 兼容,这样您就可以进行简单的比较或范围查询。如果这绝对不是您的选择,并且您使用的是相对较新版本的 MySQL (5.7.8+) 和 InnoDB,您可以创建一个虚拟列,并在其上创建 effectively create a functional index。
ON DATE_FORMAT(t.ticket_date, '%Y%m%d') = d.date_key
当您像这样在 t.ticket_date 列上使用函数时,这将永远不会使用索引。
FORCE INDEX 不会神奇地将非 sargable 表达式变成 sargable 表达式。它只是暗示优化器假设 table-scan 是无限昂贵的。因此,优化器会说,"well, that sucks for you, because this join expression needs to do a table-scan."
一种解决方案是以通用格式存储 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);
这可能更接近有效查询,并且应该更快一些,至少在 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;
我有这个查询:
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 在票证 table 上使用 ALL 类型查找。
索引:
解释计划:
我试过使用 FORCE INDEX FOR JOIN 来尝试让它从日期开始索引,但它似乎没有接受提示。
dwDimDate 是一个包含一年中第几天的日期维度,因此在这种情况下,我认为限制为 365 天会很快,然后找到该日期范围内的所有工单。在该日期范围内应该只有大约 5000 张门票。
如有任何帮助,我们将不胜感激。我不知道如何弄清楚采用什么策略来删除“ALL”查找。想知道以后怎么弄,如果你能帮我“授人以渔”就好了。
编辑 该查询目前需要 11 秒才能完成 运行,这将成为生产环境中的一个问题。
问题是您正在尝试连接列的函数,而不是列本身的值。因此,它无法使用您在 ticket_date
上的索引来执行连接。
理想情况下,您应该确保 ticket_date
的格式与 date_key 兼容,这样您就可以进行简单的比较或范围查询。如果这绝对不是您的选择,并且您使用的是相对较新版本的 MySQL (5.7.8+) 和 InnoDB,您可以创建一个虚拟列,并在其上创建 effectively create a functional index。
ON DATE_FORMAT(t.ticket_date, '%Y%m%d') = d.date_key
当您像这样在 t.ticket_date 列上使用函数时,这将永远不会使用索引。
FORCE INDEX 不会神奇地将非 sargable 表达式变成 sargable 表达式。它只是暗示优化器假设 table-scan 是无限昂贵的。因此,优化器会说,"well, that sucks for you, because this join expression needs to do a table-scan."
一种解决方案是以通用格式存储 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);
这可能更接近有效查询,并且应该更快一些,至少在 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;