涉及视图和函数的查询不能使用索引
Query involving views and functions can't use index
我有以下 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
和上面原来的形式实现,查询不能利用索引,使用顺序扫描。只是为了提供更多细节,视图 (a_view) 涉及一个大的 table (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 函数是规划器的 blackbox。与使用函数相比,如果您只使用常量列表,规划器将获得更多关于谓词的信息。在这种情况下,规划器必须使用一些默认规则,这些规则可能对您的情况来说太过分了。
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 会产生相当大的负面影响。
您可以将函数重写为 return 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 查询(为便于阅读而简化):
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
和上面原来的形式实现,查询不能利用索引,使用顺序扫描。只是为了提供更多细节,视图 (a_view) 涉及一个大的 table (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 函数是规划器的 blackbox。与使用函数相比,如果您只使用常量列表,规划器将获得更多关于谓词的信息。在这种情况下,规划器必须使用一些默认规则,这些规则可能对您的情况来说太过分了。
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 会产生相当大的负面影响。您可以将函数重写为 return
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)