当第二个表添加到查询时,MySQL日期范围idex被忽略

时间:2018-08-07 14:39:03

标签: mysql indexing range

长时间潜伏,第一次发问。 ;-)

对于Win64(x86_64),使用PHP 5.6和MySQL Ver 14.14 Distrib 5.6.41,是的,我知道有点落后于时代,我们正在努力进行更新。但这就是我们现在的位置。 ;-)

有关所提问题的更新: 索引在CreateDate上。我以为该列为DateTime可能会有问题,所以我创建了另一个仅是日期的列,在该列上设置了索引并重试,但没有任何效果。

ulc总共有8965行。带有索引搜索3787 等有9530行。在不使用索引的查询中,它在搜索第一个查询的主键时只搜索一行。

比较日期的格式似乎无关紧要。我尝试了各种格式,包括简单的“ 2018-01-01 {00:00:00}”。没有变化。

我有一个我认为很奇怪的东西,但是我怀疑对于这里的某个人来说,这将是“ du!”一。我有一个查询,其中包含主表的日期范围,然后根据第一个表的一组唯一ID从其他表中获取其他数据位。不用担心,我将在下面提供示例。当我仅在主表上进行搜索时,范围索引会按预期工作,并且仅搜索相关行。但是,当我使用ON子句添加到下一个表时,它会忽略索引并搜索主表的所有行。如果我忽略了on子句,它将返回到正确使用索引的状态。我尝试使用FORCE INDEX(忽略USE),尽管这使它使用了索引,但会降低查询速度。无论如何,这是查询:

作品:

select CreateDate
from ulc
Inner Join et
WHERE ulc.CreateDate >= STR_TO_DATE("01/01/2018", "%m/%d/%Y")
AND ulc.CreateDate <= STR_TO_DATE("08/02/2018", "%m/%d/%Y")

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   SIMPLE  ulc     range   index_CreateDate    index_CreateDate    5   NULL    3787    Using where; Using index
1   SIMPLE  et  index   NULL    index_BankProcessorProfile  5   NULL    9530    Using index; Using join buffer (Block Nested Loop)

不起作用:

select CreateDate
from ulc
Inner Join et on et.TranID = ulc.TranID
WHERE ulc.CreateDate >= STR_TO_DATE("01/01/2018", "%m/%d/%Y")
AND ulc.CreateDate <= STR_TO_DATE("08/02/2018", "%m/%d/%Y")

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra   
1   SIMPLE  ulc     ALL     TranID,index_CreateDate     NULL    NULL    NULL    8965    Using where
1   SIMPLE  et  eq_ref  PRIMARY     PRIMARY     8   showpro.ulc.TranID  1   Using index

对于第二个,我刚刚添加了on et.TranID = ulc.TranID

此外,如果我将其从范围更改为特定日期,则索引也可以正常工作。

3 个答案:

答案 0 :(得分:0)

仅在此处猜测没有更多数据,但是向JOIN添加新表会更改数据分布。

因此,如果在第一种情况下WHERE条件返回的数据可能只是一小部分(相对),那么在第二种情况下,优化器决定不使用索引即可获得更快的结果,因为相同的条件可能不太一样选择性地处理新一批数据。

为总计和基于查询的两个查询添加表定义和COUNT,以获得更好的答案。

答案 1 :(得分:0)

如果您在查询中使用DateTime,建议在地方类中使用“ YYYY-MM-DD HH:MM:SS”

如果在查询中使用日期,则建议在where类中使用格式“ YYYY-MM-DD”。您已使用STR_TO_DATE(“ 01/01/2018”,“%m /%d /% Y”),将其强制转换为“ 2018-01-01”

您尝试使用EXPLAIN查找查询的复杂性

explain select CreateDate
from ulc
Inner Join et on et.TranID = ulc.TranID
WHERE ulc.CreateDate >= STR_TO_DATE("01/01/2018", "%m/%d/%Y")
AND ulc.CreateDate <= STR_TO_DATE("08/02/2018", "%m/%d/%Y")

您可以检查et.TranID和ulc.TranID是否具有正确的索引

答案 2 :(得分:0)

(由于您还没有提供SHOW CREATE TABLE,因此我不得不在某些方面进行猜测)。作为“长期潜伏者”,您应该已经意识到这一点。)< / p>

首先猜测是TranID不是PRIMARY KEY的{​​{1}}吗?

解决方案是在ulc上添加一个“复合” INDEX(CreateDate, TranID)。 (实际上,您应该替换现有的ulc(第二个猜测是您现在有了该索引。)

现在,我将尝试解释为什么第一个查询对INDEX(CreateDate)感到满意,而第二个查询却不满意。

第一个查询中,INDEX(CreateDate)是“覆盖”索引。也就是说,该索引包含INDEX(CreateDate)所需的ulc all 列。因此,几乎可以保证使用索引比扫描表更好。这将是该索引的“范围索引扫描”。

第二个查询同时需要SELECTCreateDate,因此您的索引不会被“覆盖”。有两种方法可以执行查询的第一部分。但是首先,请注意(在InnoDB中)二级索引具有TranID的所有列(第三个猜测:它是PRIMARY KEY)。

  • 索引的范围扫描。但是,为了获取(id),它首先获取TranID,然后在id /数据中进行查找以获取TranID`。此过程比仅停留在索引中的开销更大,因此,除非估计的行数很小,否则优化器不希望这样做。
  • 由于3787/8965并非“小”,优化程序决定扫描PRIMARY KEY 8965行可能更快,从而过滤掉不需要的行。

My 建议的索引是“覆盖”的,从而避免了索引和数据之间的来回限制。因此,范围索引扫描是有效的。

您观察到使用索引转换为单个日期的情况-嗯,8965中有1行是'small',因此该索引(和弹跳)被认为是更快的方法。

关于日期格式-是的,没关系。这是因为解析器注意到ALL是可以被评估一次的常数

我的cookbook应该直接带您进入综合索引,而不必为这个问题而烦恼。

您的第一个查询是“交叉连接” ,因为它没有STR_TO_DATE("01/01/2018", "%m/%d/%Y")子句来将表关联在一起,它将返回约3500万行(9530 * 3787)。第二个查询将有大约3787行,也许更少(如果某些联接找不到匹配项)。

两个查询之间的变化很小”-永远不要这样!优化器将锁定看似无关紧要的差异。 ONSELECT CreateDate的巨大区别。我所说的关于“第一个查询”的大部分内容都会被扔掉。甚至将其更改为SELECT *也足以使您大吃一惊。如果两个表中SELECT ChangeDate, x的数据类型足够不同,则索引将变得无用。等等