SQL 不使用索引的 Firebird 条件

SQL Firebird conditional where not using index

我正在使用 Firebird SQL 2.1 版。

我有一个 sql 查询字符串,其中传递了参数。 如果传递的值不在我手上,但如果传递了,它是正确的类型(Timestamp or else..)

现在我的查询如下所示:

select r.* 
from TableA 
where r.ACTION_DATE >= iif('2019-10-09 00:00:00' is null or '2019-10-09 00:00:00' = '', r.ACTION_DATE, '2019-10-09 00:00:00')

换句话说,当传递一个值时,我想加载操作日期大于“2019-10-09 00:00:00”的所有记录,否则获取所有记录。

我在列 ACTION_DATE 上有一个索引。对于我提到的查询,未应用索引!

对于没有应用索引的条件的相同查询:

select r.* 
from TableA 
where r.ACTION_DATE >= '2019-10-09 00:00:00'

几十年来没有使用 Interbase/Firebird,但我的猜测是右侧不是 sargable

DBMS首先要得到行,得到r.ACTION_DATE的值,计算右边的表达式,然后才能确定是否保留结果中的行,此时使用索引为时已晚(我们已经检索到该行,因此无需通过索引查找)。

尝试对表达式进行预求值,然后在 WHERE 子句中使用结果值(当然还有参数绑定等)。


另一件需要注意的事情是,索引只有在选择性足够好时才有用。如果检索的行太多(根据经验:超过整个 table 的 10%),那么即使索引可用,线性遍历整个 table 通常会更快。

列上的表达式通常会扼杀索引的使用。某些数据库可以处理显式 OR:

select r.* 
from TableA 
where r.ACTION_DATE >= '2019-10-09 00:00:00' OR
      '2019-10-09 00:00:00' IS NULL;

否则UNION ALL能搞定:

select r.* 
from TableA r
where r.ACTION_DATE >= '2019-10-09 00:00:00' 
union all
select r.*
from TableA r
where '2019-10-09 00:00:00' IS NULL;

请注意,如果值为 NULL,则第一个子查询 returns 没有行,因此没有重复行的危险。