如何强制PostgreSQL使用某个索引?

时间:2019-06-13 17:22:40

标签: postgresql query-performance database-indexes

我的表cache上有两个Postgres索引,都在字段jsonbdate的{​​{1}}列上。

第一个处理不可变函数,该函数接受文本字段并将其转换为condition类型。

第二个仅在date上创建。

因此,当我尝试第二个索引时,它将btree索引转换为位图索引,并且以某种方式比第一个索引慢,这需要另外两个步骤,但仅使用索引扫描。

我有两个问题:为什么和如何?

为什么第一个只使用索引,而第二个为什么由于某种原因使用位图呢?以及如何强制PostgreSQL仅使用索引而不使用第二个索引上的位图,因为我不想使用该函数。

如果还有其他解决方案,请给我提示,因为我无权在服务器上安装软件包。

功能索引:

text

文本索引:

create index cache_ymd_index on cache (
   to_yyyymmdd_date(((data -> 'Info'::text) ->> 'Date'::text)::character varying),
   ((data -> 'Info'::text) ->> 'Condition'::text)
)  where (((data -> 'Info'::text) ->> 'Condition'::text) = '3'::text);

函数本身:

create index cache_data_index on cache (
   ((data -> 'Info'::text) ->> 'Date'::text),
   ((data -> 'Info'::text) ->> 'Condition'::text)
) where (((data -> 'Info'::text) ->> 'Condition'::text) = '3'::text);

函数索引的分析条件:

create or replace function to_yyyymmdd_date(the_date character varying) returns date
    immutable language sql
as
$$
select to_date(the_date, 'YYYY-MM-DD')
$$;

文本索引的分析条件:

Index Scan using cache_ymd_index on cache  (cost=0.29..1422.43 rows=364 width=585) (actual time=0.065..66.842 rows=71634 loops=1)
  Index Cond: ((to_yyyymmdd_date((((data -> 'Info'::text) ->> 'Date'::text))::character varying) >= '2018-01-01'::date) AND (to_yyyymmdd_date((((data -> 'Info'::text) ->> 'Date'::text))::character varying) <= '2020-12-01'::date))
Planning Time: 0.917 ms
Execution Time: 70.464 ms

1 个答案:

答案 0 :(得分:1)

“位图索引扫描”也是索引扫描。如果必须访问更大比例的表块,这是PostgreSQL通常选择的方法,因为在这种情况下,它效率更高。

对于像您这样的索引范围扫描,对此有两种可能的解释:

  1. ANALYZE已在创建两个索引之间运行,因此PostgreSQL知道一种情况下索引值的分布,而另一种情况下则不知道。

    要弄清情况,请运行

    ANALYZE cache;
    

    ,然后再次尝试两个语句。也许现在的计划更加相似。

  2. 这些语句在两个包含相同数据的不同表上运行,但是它们在物理上的排列方式不同,因此correlation在一个表上是好的,但在另一个表上是坏的。如果相关性接近1或-1,则索引扫描会更便宜。否则,位图索引扫描是最好的方法。

    由于您在两种情况下都表示它是同一张表,因此可以排除这种解释。

索引的第二列是多余的;您应该忽略它。 否则,您的两个索引应大致相同。

当然,如果首先在表中定义了date列,那么所有这些都会更好地工作...