表达式与部分索引的统计数据不一致

时间:2017-06-17 22:32:53

标签: postgresql postgresql-9.6

[x86_64-pc-linux-gnu上的PostgreSQL 9.6.1,由gcc编译(Debian 6.2.0-10)6.2.0 20161027,64位]

我有一个包含时间戳范围的表:

create table testing.test as 
select tsrange(d, null) ts from 
generate_series(timestamp '2000-01-01', timestamp '2018-01-01', interval '1 minute') s(d);

我需要运行以下查询:

select * 
from testing.test 
where lower(ts)> '2017-06-17 20:00:00'::timestamp and upper_inf(ts)

解释没有索引的表的分析结果:

Seq Scan on test  (cost=0.00..72482.26 rows=1052013 width=14) (actual time=2165.477..2239.781 rows=283920 loops=1)
  Filter: (upper_inf(ts) AND (lower(ts) > '2017-06-17 20:00:00'::timestamp without time zone))
  Rows Removed by Filter: 9184081
Planning time: 0.046 ms
Execution time: 2250.221 ms

接下来,我将添加以下部分索引:

create index lower_rt_inf ON testing.test using btree(lower(ts)) where upper_inf(ts);    
analyze testing.test;

解释具有部分索引的表的分析结果:

Index Scan using lower_rt_inf on test  (cost=0.04..10939.03 rows=1051995 width=14) (actual time=0.037..52.083 rows=283920 loops=1)
  Index Cond: (lower(ts) > '2017-06-17 20:00:00'::timestamp without time zone)
Planning time: 0.156 ms
Execution time: 62.900 ms                                                                                                         

SELECT null_frac, n_distinct, correlation FROM pg_catalog.pg_stats WHERE tablename = 'lower_rt_inf'

null_frac |n_distinct |correlation |
----------|-----------|------------|
0         |-1         |1           |

然后我创建一个类似于前一个的索引,但没有部分条件:

create index lower_rt_full ON testing.test using btree(lower(ts));
analyze testing.test;

现在使用相同的索引,但成本/行不同:

Index Scan using lower_rt_inf on test  (cost=0.04..1053.87 rows=101256 width=14) (actual time=0.029..58.613 rows=283920 loops=1)
  Index Cond: (lower(ts) > '2017-06-17 20:00:00'::timestamp without time zone)
Planning time: 0.280 ms
Execution time: 71.794 ms                                                                                                       

还有一点:

select * from testing.test where lower(ts)> '2017-06-17 20:00:00'::timestamp;

Index Scan using lower_rt_full on test  (cost=0.04..3159.52 rows=303767 width=14) (actual time=0.036..64.208 rows=283920 loops=1)
  Index Cond: (lower(ts) > '2017-06-17 20:00:00'::timestamp without time zone)
Planning time: 0.099 ms
Execution time: 78.759 ms

如何有效地为表达式使用部分索引?

2 个答案:

答案 0 :(得分:0)

这里发生的是索引lower_rt_full的统计数据用于估计行数,但lower_rt_inf是部分索引,不是。

由于函数是PostgreSQL的黑盒子,它不知道lower(ts)的分布并且使用了错误的估计。

创建lower_rt_full并分析表后,PostgreSQL对此分布有一个很好的了解,并且可以更好地估计。即使索引不用于执行查询,它也用于查询计划。

由于upper_inf也是一个函数(黑盒子),如果你有一个索引ON test (upper_inf(ts), lower(ts)),你会得到更好的估计。

有关为什么不考虑部分索引来估计结果行数的解释,请参阅examine_variable中的backend/utils/adt/selfuncs.c中的此注释,该 * Has it got stats? We only consider stats for * non-partial indexes, since partial indexes probably * don't reflect whole-relation statistics; the above * check for uniqueness is the only info we take from * a partial index. 会尝试查找有关表达式的统计数据:

InternalsVisibleTo

答案 1 :(得分:0)

感谢您的回答。 在索引中使用函数的问题(低(rt))? 或者在函数用于部分索引的条件下。

因为,如果我添加一个单独的字段"最新":

alter table testing.test add column latest boolean;
update testing.test set latest = upper_inf(ts);
create index lower_latest_rt ON testing.test using btree(lower(ts)) where latest = true;
alter index testing.lower_latest_rt alter column lower set statistics 1000;
analyze testing.test;

执行以下查询:

select * 
from testing.test 
where lower(ts)> '2017-06-17 20:00:00'::timestamp and latest = true

我有结果:

Index Scan using lower_latest_rt on test  (cost=0.04..11406.44 rows=285833 width=23) (actual time=0.027..178.054 rows=283920 loops=1)
Index Cond: (lower(ts) > '2017-06-17 20:00:00'::timestamp without time zone)
Planning time: 1.788 ms
Execution time: 188.481 ms