[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
如何有效地为表达式使用部分索引?
答案 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