连接

时间:2018-06-08 17:46:44

标签: mysql indexing sql-execution-plan query-planner

我知道有几个问题与此类似,但我发现的问题与我的问题没有直接关系。

一些初始上下文:我有一个名为ft_booking的事实表,大约有10MM的记录。我有一个名为dm_date的维度,大约有11k条记录,即日期。像往常一样,这些表通过外键相关联。表ft_booking中有3个日期外键,一个用于登机,一个用于预订,另一个用于取消。所有列都具有相同的定义,并且每个列的不同记录数量相似(每列中的差异值从2.5k到3k不等)。

我去了:

EXPLAIN SELECT
*
FROM dw.ft_booking b
LEFT JOIN dw.dm_date db ON db.sk_date = b.fk_date_booking
WHERE date (db.date) = '2018-05-05'

enter image description here

正如您所看到的,索引正在预订中使用,并且查询运行得非常快,即使在我的过滤器中,我使用了date()函数。为简洁起见,我将使用列fk_date_boarding声明同样的情况。但是,看看这个:

EXPLAIN SELECT
*
FROM dw.ft_booking b
LEFT JOIN dw.dm_date db ON db.sk_date = b.fk_date_cancellation
WHERE date (db.date) = '2018-05-05';

enter image description here

出于某种神秘的原因,计划者选择不使用索引。现在,我理解在列类上使用某些函数会强制数据库执行全表扫描,以便能够在列上应用该函数,从而绕过索引。但是,在这种情况下,该函数不在实际的外键列上,这是预订表中的查找应该是在哪里。

如果我删除了date()函数,索引将按照预期在任何列中使用。那么,有人可能会说,"好吧,为什么你不能摆脱date()函数?" - 我使用了一个接口,它允许用户使用图形界面来构建查询而不需要知道MySQL,并且该工具当前的一个限制是它总是在构建不直接写入的查询时使用date()函数。 MySQL - 因此,我无法删除我正在运行的查询中的函数。

实际问题:为什么MySQL在前两种情况下使用索引,但在后一种情况下不使用索引,考虑到所有列的不同值的数量几乎相同,并且它们具有完全相同的定义从名字?我在这里错过了什么吗?

编辑:Here是所涉及的每个表的CREATE语句。还有一些,但我们只需要这里的表ft_booking和dm_date(文件的前两个表)。

1 个答案:

答案 0 :(得分:1)

您正在“隐藏date函数调用”。如果db.date被声明为DATE,那么

    date (db.date) = '2018-05-05'

可以简单地

    db.date = '2018-05-05'

如果db.date被声明为DATETIME,则更改为

        db.date >= '2018-05-05'
    AND db.date  < '2018-05-05' + INTERVAL 1 DAY

在任何一种情况下,请确保db.date上有索引。

如果“我有一个名为dm_date的维度”,则表示您构建了一个维度表来保存日期,然后您JOINing到主表,其中包含id,...说白了,不要这样做!不要将DATEDATETIMEFLOAT或其他数字值等“连续”事物标准化。

如果您需要进一步讨论,请提供SHOW CREATE TABLE相关表格。 (请使用文字,而不是屏幕截图。)

<强>为什么?

简单的答案是,优化器不知道如何解开任何功能。也许它可以;也许它应该。但事实并非如此。也许答案是不想看到如何使用函数结果......与DATE进行比较?针对DATETIME?被用作字符串?其他

尽管如此,我认为真正的性能杀手是dm_date的存在,而不是索引和使用主表中的日期。

此外,主表比它需要的还要大! fk_date_booking是一个4字节INT SIGNED,而不是3字节DATE