Postgres IN子句中有许多值不使用部分索引

时间:2017-12-26 04:33:11

标签: sql postgresql indexing sql-in

我正在使用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;

EXPLAINseq_scan子句中显示超过100个ID号的WHERE

为什么会这样? 有没有办法解决它?

1 个答案:

答案 0 :(得分:2)

您正在运行outdated version of 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);

请参阅: