长时间潜伏,第一次发问。 ;-)
对于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
此外,如果我将其从范围更改为特定日期,则索引也可以正常工作。
答案 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 列。因此,几乎可以保证使用索引比扫描表更好。这将是该索引的“范围索引扫描”。
第二个查询同时需要SELECT
和CreateDate
,因此您的索引不会被“覆盖”。有两种方法可以执行查询的第一部分。但是首先,请注意(在InnoDB中)二级索引具有TranID
的所有列(第三个猜测:它是PRIMARY KEY
)。
(id)
,它首先获取TranID
,然后在id
/数据中进行查找以获取TranID`。此过程比仅停留在索引中的开销更大,因此,除非估计的行数很小,否则优化器不希望这样做。PRIMARY KEY
8965行可能更快,从而过滤掉不需要的行。My 建议的索引是“覆盖”的,从而避免了索引和数据之间的来回限制。因此,范围索引扫描是有效的。
您观察到使用索引转换为单个日期的情况-嗯,8965中有1行是'small',因此该索引(和弹跳)被认为是更快的方法。
关于日期格式-是的,没关系。这是因为解析器注意到ALL
是可以被评估一次的常数。
我的cookbook应该直接带您进入综合索引,而不必为这个问题而烦恼。
您的第一个查询是“交叉连接” ,因为它没有STR_TO_DATE("01/01/2018", "%m/%d/%Y")
子句来将表关联在一起,它将返回约3500万行(9530 * 3787)。第二个查询将有大约3787行,也许更少(如果某些联接找不到匹配项)。
“ 两个查询之间的变化很小”-永远不要这样!优化器将锁定看似无关紧要的差异。 ON
与SELECT CreateDate
的巨大区别。我所说的关于“第一个查询”的大部分内容都会被扔掉。甚至将其更改为SELECT *
也足以使您大吃一惊。如果两个表中SELECT ChangeDate, x
的数据类型足够不同,则索引将变得无用。等等