为什么Firebird 2.5.5及更高版本不在WHERE IN子查询中使用索引?

时间:2017-03-29 01:57:32

标签: sql firebird firebird2.5

我的问题始于一个令人难以置信的基本和正确索引的更新语句,但没有正确使用索引,并且随着记录数增加而开始在生产中陷入困境。我已经看到使用IN在旧Firebird版本中使用子查询的旧帖子根本没有优化,但我使用2.5.5,然后升级到2.5.7只是为了确保,问题仍然存在似乎正在发生。

我的理解是“在Firebird的后期版本中解决了这个问题”,文章日期早在2.5之前,但这不是我所看到的,我不知道为什么。

我能够使用MERGE语句在我的下一个应用程序版本中解决UPDATE语句的性能问题,但我现在感到困惑的是为什么这个案例不能像我期望的那样工作,如果有什么我可以做的修理它,或者是否应该避免引擎的限制。

采取这种模式(我已经完成了方言1和3 - 虽然这是3):

CREATE TABLE TEST 
(
  TESTID                INTEGER         NOT NULL,
  TESTNAME              VARCHAR(    50)  COLLATE NONE,
  DETAILID              INTEGER
);
CREATE TABLE DETAIL 
(
  DETAILID                INTEGER         NOT NULL,
  DETAILNAME              VARCHAR(    50)  COLLATE NONE
);

CREATE ASC INDEX I_TEST_DETAILID ON TEST (DETAILID);

我在每个表中创建了10k条记录,整数字段编号为1到10k。然后我尝试一个超级简单的SELECT。请记住,我的原始语句是一个UPDATE语句,它与FROM相同 - 我在SELECT中理解我可以做一个JOIN而不是为了更好的性能,但根据我读过的所有内容,这是假设的现在正常工作,但我无法得到我期望的PLAN结果:

SELECT
  T.*
FROM
  TEST T
WHERE
  T.DETAILID IN (SELECT D.DETAILID FROM DETAIL D WHERE D.DETAILID BETWEEN 1 AND 100)

这导致:

PLAN (D INDEX (PK_DETAIL))
PLAN (T NATURAL)

T的PLAN始终是自然的,因此每次读取整个10k记录。如果我只运行子查询部分,它只能正确读取100条记录并使用DETAIL主键。

在很多地方,包括这里,我都会使用EXISTS代替。所以,我试着这样做:

SELECT
  T.*
FROM
  TEST T
WHERE
  EXISTS (SELECT 1 FROM DETAIL D WHERE (D.DETAILID = T.DETAILID) AND (D.DETAILID BETWEEN 1 AND 100))

同样的结果。 T是PLAN Natural,读取了10k记录。

所以,我完全失去了。我已经尝试了我能想到的一切,包括不同的方言,不同的2.5版本,以及不同的索引(外键等),并确保正确计算选择性。我已经搜索了Firebird bug跟踪器,试图看看这是一个开放的问题,但我找到的所有这些都是无限复杂的,似乎不适用于这个简单的例子。如果这应该有效,我不明白为什么,为什么它不适合我。

而且,尽管我通过进行合并(它在计划和记录读取中的行为与预期相同)确实改进了我的UPDATE,但命令更复杂,并且不一定是您想要这样做时的第一件事一个简单的操作。更不用说了,我希望修改一个生产系统而不改变我的代码中的SQL,但我觉得在这些发现之后是不可能的。

更新 为了澄清评论中的一些问题。导致问题的原始查询与此相当:

UPDATE  
  TEST T
SET
  T.TESTNAME = "SomeValue"
WHERE
  T.DETAILID IN (SELECT D.DETAILID FROM DETAIL D WHERE D.DETAILID BETWEEN 1 AND 100)

我只将问题的查询转换为SELECT,以便于复制和测试。 UPDATE和DELETE语句不能使用连接。 SELECT中JOIN的性能确实解决了问题,但子查询是我在UPDATE和DELETE中的选项。

其他答案:

  • 我只放2.5.5,因为那是我正在使用的版本。我不知道它是否在以前的版本中有效,并不意味着暗示它在2.5.5中停止工作。
  • 删除子查询上的WHERE不会更改PLAN。
  • 所有ID都是唯一的,只需1到10k的衬垫,因此BETWEEN 1 AND 100只能提供100行。
  • 我之所以提到为什么我认为EXISTS应该有效:Why Index is not used with subquery
  • 我在新版本中对此进行了修改(请注意,网站现在似乎已关闭):http://www.firebirdfaq.org/faq37/

由于最后一次引用没有提升,所以它就是这样说的:

  

为什么我的查询使用IN或NOT IN会变慢?

     

这是Firebird优化器的问题。在较新的Firebird版本中   IN的问题是固定的,但NOT IN的问题仍然存在。在   无论如何,使用EXISTS和NOT EXISTS是安全的,而且通常更快   代替。可以重写每个IN和NOT IN查询以使用EXISTS和   不存在。

     

示例:

select * from employee e
where e.emp_no in (select s.emp_no from SALARY_HISTORY s);

-- rewritten with EXISTS:

select * from employee e
where exists (select 1 from SALARY_HISTORY s where e.emp_no = s.emp_no);

不确定日期,但我几乎肯定它早于2.5,我肯定没有看到它在我的测试中提到的相同结果(它在以后的版本中得到修复,也没有说EXISTS应该可以正常工作)。

0 个答案:

没有答案
相关问题