使用JOIN时不使用Firebird索引,为什么?

时间:2016-02-19 15:12:12

标签: firebird firebird2.5

我正在使用FB 2.5.5,而我试图理解为什么一个非常简单的查询不使用索引,因此需要永远执行。我已经阅读了很多关于为什么现有索引可能会被查询优化器忽略的文章,但是我不理解它在我的情况下会如何发生。我重新计算了IB Expert中所有索引的选择性,并且我还对数据库进行了备份/恢复,以确保我没有遗漏任何东西。

IB Expert显示的指数选择性约为0,000024 - 远离1:

echo $dom->saveHTML();

我查询的表包含约。 2M记录:

CREATE INDEX TVERSIONS_IDX_LASTMODDATE ON TVERSIONS (LASTMODDATE)

我尝试根据LASTMODDATE字段(TIMETSAMP,由TVERSIONS_IDX_LASTMODDATE索引)获取所有记录。查询的过度简化版本将是:

SELECT COUNT(ID) FROM TVERSIONS
2479518

在这种情况下,执行计划显示实际使用了索引:

SELECT COUNT(ID) FROM TVERSIONS WHERE LASTMODDATE > :TheDate

...并且非常快速地获取与条件匹配的记录:

Plan
PLAN (TVERSIONS INDEX (TVERSIONS_IDX_LASTMODDATE))

现在,"真实"查询使用LASTMODDATE上的相同条件获取相同的字段,但在3个表中添加了JOIN:

------ Performance info ------
Prepare time = 172ms
Execute time = 16ms <----
Avg fetch time = 16,00 ms
Current memory = 2 714 672
Max memory = 10 128 480
Memory buffers = 90
Reads from disk to cache = 57
Writes from cache to disk = 0
Fetches from cache = 387

现在查询计划不再使用索引

SELECT COUNT(ID) FROM TVERSIONS
    JOIN TFILES ON TFILES.ID = TVERSIONS.FILEID
    JOIN TROOTS ON TROOTS.ID = TFILES.ROOTID
    JOIN TUSERSBACKUPS ON TROOTS.BACKUPID = TUSERSBACKUPS.BACKUPID
    WHERE TUSERSBACKUPS.USERID= :UserID
    AND TVERSIONS.LASTMODDATE >:TheDate

没有任何意外执行时间慢得多(约1分钟):

Plan
PLAN JOIN (TUSERSBACKUPS INDEX (RDB$FOREIGN4), TROOTS INDEX (RDB$FOREIGN3), TFILES INDEX (RDB$FOREIGN2), TVERSIONS INDEX (RDB$FOREIGN6))

换句话说,搜索WHOLE表比搜索JOIN返回的行子集要快得多。

我无法理解为什么不再使用LASTMODDATE字段上的索引只是因为我添加了join子句。索引的选择性很好,查询非常简单。我错过了什么?

2 个答案:

答案 0 :(得分:2)

似乎Firebird决定使用索引TUSERSBACKUPS.USERID=:UserID开始条件RDB$FOREIGN4。可能它发生的原因是你在这里有平等,而条件TVERSIONS.LASTMODDATE >:TheDate你有不平等可能会导致更大的记录集(例如,如果TheDate是200年前的日期,它将包括整个表)。

要强制Firebird使用您(但不是其优化程序)更喜欢的计划 - 使用PLAN子句,请参阅13.2 Creating Function Objects

答案 1 :(得分:0)

我想我已经明白发生了什么,并且......我想这是我的错。

我忘记了我查询的表格已被“非规范化”#34;避免这么长的JOIN。有问题的查询确实可以用更短的方式重写:

SELECT COUNT(TVERSIONS.ID) FROM TVERSIONS
    JOIN TUSERSBACKUPS ON TUSERSBACKUPS.BACKUPID = TVERSIONS.RD_BACKUPID
    WHERE TUSERSBACKUPS.USERID= :UserID
    AND TVERSIONS.LASTMODDATE >:TheDate

这个正确使用我之前设置的索引,执行时间非常短。

我的印象是,当Firebird检测到您故意使用次优路径访问表格中的记录时,它甚至不会尝试使用您的索引并让您自己射击...... / p>

无论如何,问题解决了。谢谢大家的建议。