Firebird 2.5 SQL查询执行计划不使用带有OR语句的索引

时间:2016-11-29 15:43:48

标签: sql query-optimization parent-child firebird2.5 sql-execution-plan

首先:我不是数据库专家,所以如果这个问题很简单,请放纵......

我在子表CHILD上查询主表PARENT中的某些值,以确定是否要加载该条目。

查询看起来像这样

SELECT C.*, P.DATE, P.STATUS 
FROM CHILD C, PARENT P
WHERE C.PARENT_ID = P.ID 
AND (P.DATE > '01.01.2015' OR (P.STATUS <> 1 AND P.STATUS <> 9));

我故意选择STATUS值来强调我需要使用不等式作为我需要选择的状态值是非连续的。

我在字段PARENT.ID的{​​{1}}上有一个外键,并在CHILD.PARENT_ID上创建了一个索引。我还在字段CHILD.PARENT_IDPARENT上的表DATE上创建了一个索引。

现在,当我将STATUS替换为OR AND使用CHILD上的索引而PARENT_ID使用PARENT上的索引时这是我所期待的。

但在使用DATE, STATUS时,查询会使用OR上的自然计划和CHILD上的主键索引。

如果我仅将查询应用于父表,则会出现相同的情况:

PARENT.ID

有没有办法优化这样的查询以比自然计划更好地使用?

2 个答案:

答案 0 :(得分:2)

如果你写的是&#39;或&#39;,每个P.DATE都可能会被击中。每个P.STATUS也可能会受到重创。如果你想使用索引,这不是很好的先决条件。

在这里,您必须帮助您的系统并提出2个单独的问题,并将它们与UNION连接起来。像

SELECT C.*, P.DATE, P.STATUS 
  FROM CHILD C, PARENT P 
 WHERE C.PARENT_ID = P.ID 
   AND P.DATE > '01.01.2015' 
UNION
SELECT C.*, P.DATE, P.STATUS 
  FROM CHILD C, PARENT P 
 WHERE C.PARENT_ID = P.ID 
   AND P.STATUS <> 1 
   AND P.STATUS <> 9;

注意:如果P.STATUS的大多数值不等于1且不等于9,那么你的性能仍然不佳。想象一下,你会在一本书中找到所有单词索引的单词&#39;和&#39;。它将在每一页;按顺序阅读本书比使用索引更快。

答案 1 :(得分:2)

优化器使用索引可能非常困难。首先,使用join s:

重写查询
SELECT C.*, P.DATE, P.STATUS 
FROM CHILD C JOIN
     PARENT P
     ON C.PARENT_ID = P.ID 
WHERE P.DATE > '2015-01-01' OR P.STATUS NOT IN (1, 9);

您可以使用UNION ALL重写此内容:

SELECT C.*, P.DATE, P.STATUS 
FROM CHILD C JOIN
     PARENT P
     ON C.PARENT_ID = P.ID 
WHERE P.DATE > '2015-01-01'
UNION ALL
SELECT C.*, P.DATE, P.STATUS 
FROM CHILD C JOIN
     PARENT P
     ON C.PARENT_ID = P.ID 
WHERE P.DATE <= '2015-01-01' AND   -- This condition prevents overlaps
      P.STATUS NOT IN (1, 9);

子查询现在可以使用PARENT(DATE, ID)PARENT(STATUS, DATE, ID)上的索引。

但是,过滤结果实际上会使查询更快,这一点尚不清楚。这取决于过滤器的选择性。