【问题标题】:MySQL Index and Explain PlanMySQL 索引和解释计划
【发布时间】:2018-05-06 17:57:00
【问题描述】:

我有这个问题:

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 类型查找。

索引:

解释计划:

我尝试使用 FORCE INDEX FOR JOIN 来尝试让它索引日期,但它似乎没有得到提示。

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

任何帮助将不胜感激。我不知道如何弄清楚要采用什么策略来删除“ALL”查找。我想知道以后怎么做,所以如果你能帮助“教我钓鱼”,那就太好了。

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

【问题讨论】:

  • 你真的需要LEFT吗?
  • 这是不正确的格式 - 当给定日期有多个票时,应该传递来自 tickets 的哪些值??

标签: mysql sql-execution-plan explain


【解决方案1】:

这可能更接近有效查询,并且应该更快一些,至少在 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;

【讨论】:

    【解决方案2】:
    ON DATE_FORMAT(t.ticket_date, '%Y%m%d') = d.date_key 
    

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

    FORCE INDEX 不会神奇地将 non-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);
    

    【讨论】:

    • 完美,谢谢!事后看来,这似乎很明显,但我学到了一些东西。我修改了我的日期维度表,并以门票表的格式添加了一个列,现在我已降至 0.156 秒!
    【解决方案3】:

    问题是您试图加入列的函数,而不是列本身的值。因此,它不能使用您在ticket_date 上的索引来执行连接。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-14
      • 1970-01-01
      • 2015-06-09
      • 2012-12-26
      • 2012-05-21
      • 2020-10-20
      • 2023-03-10
      • 1970-01-01
      相关资源
      最近更新 更多