长期潜伏者,第一次提问者
使用php5.6和mysql版本14.14发行版5.6.41,对于win64(x86\u64)是的,我知道有点落后于时代,我们正在努力更新。但那就是我们现在的处境
问题更新:索引在createdate上。我认为该列是datetime可能有问题,所以我创建了另一个只是日期的列,在该列上设置索引并重试,但没有任何效果。
ulc总共有8965行。使用索引搜索3787 et有9530行。在不使用索引的查询中,它在第一个查询的主键上搜索时只搜索一行。
比较日期的格式似乎无关紧要。我尝试过各种格式,包括直接的“2018-01-01{00:00:00}”。没有变化。
我有一个我认为奇怪的,但我怀疑这里的人会是一个“duh!”一个。我得到了一个查询,其中包含主表的日期范围,然后根据第一个表中的一组唯一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条答案
按热度按时间ghhaqwfi1#
只需猜测这里没有更多的数据,但添加一个新的表到连接改变了数据分布。
因此,如果在第一种情况下where条件返回的数据可能占数据的一小部分(相对而言),那么在第二种情况下,优化器决定不使用索引就可以获得更快的结果,因为相同的条件可能对新的数据批不太有选择性。
为两个查询添加表定义和计数,包括总计和基于您的查询,以获得更好的答案。
esyap4oy2#
如果在查询中使用datetime,建议在where类中使用“yyyy-mm-dd hh:mm:ss”
如果您在查询中使用date,建议在where类中使用格式“yyyy-mm-dd”。您使用str\u-to-date(“01/01/2018”,“%m/%d/%y”)将类型转换为“2018-01-01”似乎可以
您尝试使用explain查找查询的复杂性
您可以检查et.tranid和ulc.tranid是否有正确的索引
guz6ccqo3#
)有些事情我得猜一下,因为你没有提供
SHOW CREATE TABLE
. 作为一个“长期潜伏者”,你应该意识到这一点。)第一个猜测是
TranID
不是PRIMARY KEY
的ulc
?解决方法是添加一个“复合”
INDEX(CreateDate, TranID)
至ulc
. (实际上,你应该替换现有的INDEX(CreateDate)
(第二个猜测是你现在有了索引。)现在我将解释为什么第一个查询对
INDEX(CreateDate)
但第二个不是。在第一个查询中,
INDEX(CreateDate)
是一个“覆盖”索引。也就是说,这个索引包含ulc
是政府所需要的SELECT
. 因此,几乎可以保证使用索引比扫描表更好。它将是该索引的“范围索引扫描”。第二个查询需要两者
CreateDate
以及TranID
,所以您的索引将不会“覆盖”。有两种方法可以执行查询的第一部分。但首先,请注意(在innodb中)二级索引包含PRIMARY KEY
(第三种猜测:是的(id)
).索引的范围扫描。但是,为了
TranID
,它首先得到id
,然后在PRIMARY KEY
/获取tranid的数据。这个过程比简单地停留在索引中花费更多,因此优化器不想这样做,除非估计的行数“很小”。因为3787/8965不是“小的”,优化器决定它可能更快地扫描
ALL
8965行,过滤掉不需要的行。我建议的索引是“覆盖”,从而避免了索引和数据之间的来回边界。因此,范围索引扫描是有效的。
根据你的观察,切换到一个单一的日期是利用索引的——好吧,8965行中有一行是“小的”,所以索引(和跳跃)被认为是更快的方法。
至于日期的格式——是的,这无关紧要。这是因为解析器注意到
STR_TO_DATE("01/01/2018", "%m/%d/%Y")
是一个可以计算一次的常量,并且可以这样做。我的食谱应该直接带你到综合指数,而不必为这个问题挠头。
您的第一个查询是“交叉连接”,因为它没有
ON
子句将表关联在一起,它将返回大约3500万行(9530*3787)。第二个查询将有大约3787行,可能更少(如果一些连接找不到匹配项)。“两个查询之间的变化太小了”--千万别这么想!优化器将抓住看似无关紧要的差异。
SELECT CreateDate
与SELECT *
--一个巨大的不同。我所说的关于“第一个查询”的大部分内容都会被抛出。甚至改成SELECT ChangeDate, x
就足以造成一条大皱纹。如果TranID
在两个表相差足够大的情况下,索引就变得毫无用处了。等等,等等。