我有以下 postgresql 查询(为便于阅读而简化):
select *
from a_view
where a in (select * from a_function(a_input))
and b in (select * from b_function(b_input));
此查询执行得异常缓慢。
如果我独立运行两个子查询,它们会非常快。如果我运行查询写出子查询的输出,即:
select *
from a_view
where a in (394990, 393762, 393748, 1)
and b in (331142, 330946, 331228, 331325);
这也相当快。我运行 explain analyze
并以上面的原始形式实现,查询不能利用索引并使用顺序扫描。只是为了提供更多细节, View (a_view) 涉及一个大表(10+ 百万行)并且它在 (a,b) 和 (b) 上都有索引。
有没有办法帮助查询利用索引?
最佳答案
可能有两个问题:
默认情况下,任何 SRF 函数在 1000 上都有
ROWS
子句 - 计划者也期望如此。在您的示例中这是错误的。尝试将此属性设置为更合适的值(例如 10 - 太小也可能不好):postgres=# explain select * from xx(); ┌───────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞═══════════════════════════════════════════════════════════╡ │ Function Scan on xx (cost=0.25..10.25 rows=1000 width=4) │ └───────────────────────────────────────────────────────────┘ (1 row)
PLpgSQL 函数是规划器的黑匣子。与使用函数相比,如果您只使用常量列表,规划器将获得更多关于谓词的信息。在这种情况下,规划器必须使用一些默认规则,这些规则可能对您的情况来说太过分了。
postgres=# explain select * from xx where a in (10,20); ┌────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞════════════════════════════════════════════════════╡ │ Seq Scan on xx (cost=0.00..170.00 rows=2 width=4) │ │ Filter: (a = ANY ('{10,20}'::integer[])) │ └────────────────────────────────────────────────────┘ (2 rows) postgres=# explain select * from xx where a in (select * from xx()); ┌──────────────────────────────────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞══════════════════════════════════════════════════════════════════════════════════╡ │ Hash Join (cost=17.25..201.85 rows=5000 width=4) │ │ Hash Cond: (xx.a = xx_1.xx) │ │ -> Seq Scan on xx (cost=0.00..145.00 rows=10000 width=4) │ │ -> Hash (cost=14.75..14.75 rows=200 width=4) │ │ -> HashAggregate (cost=12.75..14.75 rows=200 width=4) │ │ Group Key: xx_1.xx │ │ -> Function Scan on xx xx_1 (cost=0.25..10.25 rows=1000 width=4) │ └──────────────────────────────────────────────────────────────────────────────────┘ (7 rows)
我有两个可能相同的查询,但计划完全不同,而且性能可能也完全不同。
可以解决的问题:
不要这样做 - 在 SQL 查询的关键位置(主要是
WHERE
子句)使用 plpgsql 会产生相当大的负面影响。您可以重写您的函数以返回
int[]
而不是SETOF int
。在这种情况下,规划器将使用不同的规则,性能会更好。postgres=# explain select * from xx where a = any( xx2()); ┌──────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞══════════════════════════════════════════════════════╡ │ Seq Scan on xx (cost=0.00..2770.00 rows=11 width=4) │ │ Filter: (a = ANY (xx2())) │ └──────────────────────────────────────────────────────┘ (2 rows)
如果
a_function
和b_function
的结果不依赖于内容a
和b
,然后可以通过在这些函数上设置标志IMMUTABLE
来在查询之前评估它们。然后在计划时间内评估函数并将结果用作常量 - 计划者将获得更多信息。 注意:如果先决条件为假,则结果可能是错误的。当心。-- xx2 is IMMUTABLE now postgres=# explain select * from xx where a = any( xx2()); ┌───────────────────────────────────────────────────────┐ │ QUERY PLAN │ ╞═══════════════════════════════════════════════════════╡ │ Seq Scan on xx (cost=0.00..182.50 rows=3 width=4) │ │ Filter: (a = ANY ('{30314,3783,70448}'::integer[])) │ └───────────────────────────────────────────────────────┘ (2 rows)
关于postgresql - 涉及 View 和函数的查询不能使用索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33653857/