具有许多不使用部分索引的值的 Postgres IN 子句
Postgres IN clause with many values not using partial index
我正在使用 Postgres 9.2.24。
我有一个名为 _order
的 table,大约有 100,000,000 行。 table 有一个名为 merged_id int8
的列。 _order
行中约有 2,000,000 行具有 merged_id
值,其余行为空。
我在搜索 _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;
EXPLAIN
在 WHERE
子句中显示超过 100 个 ID 号码的 seq_scan
。
这是为什么?
有什么办法可以解决吗?
您正在 运行宁 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
您的服务器配置可能存在其他问题,例如成本设置或 table 统计信息:
- Keep PostgreSQL from sometimes choosing a bad query plan
并且在创建部分索引后,不要忘记在 table 上至少 运行 ANALYZE
一次。或者,最好是 VACUUM ANALYZE
,但这对你的大 table.
来说更贵
但是,对于长值列表,可以使用更高效的查询变体:
SELECT o.*
FROM unnest('{10001 ,10002 ,10003 ....., 11000}'::int8[]) merged_id
JOIN _order o USING (merged_id);
参见:
我正在使用 Postgres 9.2.24。
我有一个名为 _order
的 table,大约有 100,000,000 行。 table 有一个名为 merged_id int8
的列。 _order
行中约有 2,000,000 行具有 merged_id
值,其余行为空。
我在搜索 _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
在 WHERE
子句中显示超过 100 个 ID 号码的 seq_scan
。
这是为什么?
有什么办法可以解决吗?
您正在 运行宁 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
您的服务器配置可能存在其他问题,例如成本设置或 table 统计信息:
- Keep PostgreSQL from sometimes choosing a bad query plan
并且在创建部分索引后,不要忘记在 table 上至少 运行 ANALYZE
一次。或者,最好是 VACUUM ANALYZE
,但这对你的大 table.
但是,对于长值列表,可以使用更高效的查询变体:
SELECT o.*
FROM unnest('{10001 ,10002 ,10003 ....., 11000}'::int8[]) merged_id
JOIN _order o USING (merged_id);
参见: