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

Posted

技术标签:

【中文标题】Firebird 2.5 SQL 查询执行计划不使用带有 OR 语句的索引【英文标题】:Firebird 2.5 SQL query execution plan not using index with OR statement 【发布时间】:2016-11-29 15:43:47 【问题描述】:

首先:我不是数据库专家,所以如果问题是微不足道的,请尽情享受......

我对子表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 上有一个外键,用于字段CHILD.PARENT_ID,并在CHILD.PARENT_ID 上创建了一个索引。我还在PARENT 字段DATESTATUS 上创建了一个索引。

现在,当我用 ANDAND 替换 OR 时,CHILD 使用 PARENT_ID 上的索引,PARENT 使用 DATE, STATUS 上的索引,这正是我所期望的。

但是当使用OR时,查询使用CHILD上的自然计划和PARENT.ID上的主键索引。

如果我只将查询应用于父表,也会发生同样的情况:

SELECT P.* FROM PARENT P WHERE (P.DATE > '01.01.2015' OR (P.STATUS <> 1 AND P.STATUS <> 9));

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

【问题讨论】:

能否请您在此处更新两个计划:Lhttps://www.brentozar.com/pastetheplan/..还有表数,两个表上的索引 请标记您正在使用的数据库。 请edit您的问题为有问题的表(包括所有索引)和执行计划添加create table 语句。 Formatted 文本 请no screen shots 【参考方案1】:

如果您写“或”,则每个 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,那么您的性能仍然会很差。想象一下,您正在看一本书,所有单词都被索引,以查找单词“and”。它将出现在每一页;按顺序阅读会比使用索引更快。

【讨论】:

【参考方案2】:

这对于优化器来说很难使用索引。首先,使用joins 重写查询:

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) 上的索引。

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

【讨论】:

以上是关于Firebird 2.5 SQL 查询执行计划不使用带有 OR 语句的索引的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# Firebird 对数据库执行选择查询并将其显示在 shell 中?

Firebird 从 2.1.3 升级到 2.5?

如何优化我的 Firebird SQL 查询?

使用 FireDAC 连接到嵌入式 Firebird 2.5

向 Firebird SQL 查询中的列添加小计

mysql-SQL优化