3

我正在使用 Postgres 9.2.24。

我有一个名为_order大约 100,000,000 行的表。该表有一个名为 的列merged_id int8。大约 2,000,000_order行有一个merged_id值,其余的为空。

我发现两种不同的 Postgres 行为,我在其中_order使用查询进行搜索

select * from _order where merged_id in ( 10001 ,10002 ,10003 ....., 11000);

如果我创建这样的索引:

create index order_merged_id_index on _order(merged_id);

无论 in 子句中有多少个 id(从 1 到 50 到 100 到 200 到 1000 的测试)EXPLAIN都显示搜索将使用index_scan.

但是,如果我改为创建此部分索引:

create index order_merged_id_index on _order(merged_id) where merged_id is not null;

EXPLAIN在子句中显示seq_scan100 多个 id 编号。WHERE

为什么是这样?
有什么办法可以解决吗?

4

1 回答 1

6

您正在运行Postgres 的过时版本。考虑尽快升级。

有很多可能的原因。我怀疑过时版本的选择性估计存在弱点。我依稀记得最多 100 个值用于涉及数组的查询计划,这些值后来得到了改进。IN表达式通常在= ANY (ARRAY[...]内部转换为 ):

无论哪种方式,您都可以通过在查询中重复部分索引的谓词来修复该行为:

SELECT * FROM _order 
WHERE merged_id IN ( 10001 ,10002 ,10003 ....., 11000)
AND   merged_id is not null;  -- logically redundant

您的服务器配置可能存在其他问题,例如成本设置或表统计信息:

并且不要忘记ANALYZE在创建部分索引后至少在您的表上运行一次。或者,最好是VACUUM ANALYZE,但这对你的大桌子来说更贵。

但是,对于长列表的值,有更有效的查询变体开始:

SELECT o.*
FROM   unnest('{10001 ,10002 ,10003 ....., 11000}'::int8[]) merged_id
JOIN   _order o USING (merged_id);

看:

于 2017-12-26T04:59:58.030 回答