繁体   English   中英

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

[英]Postgres IN clause with many values not using partial index

我正在使用Postgres 9.2.24。

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

我在搜索_order使用查询时发现了两种不同的Postgres行为

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;

EXPLAINWHERE子句中显示了seq_scan ,其中包含100个以上的id编号。

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

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

看到:

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM