postgresql - 涉及 View 和函数的查询不能使用索引

标签 postgresql plpgsql database-performance

我有以下 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) 上都有索引。

有没有办法帮助查询利用索引?

最佳答案

可能有两个问题:

  1. 默认情况下,任何 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)
    
  2. 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)
    

    我有两个可能相同的查询,但计划完全不同,而且性能可能也完全不同。

可以解决的问题:

  1. 不要这样做 - 在 SQL 查询的关键位置(主要是 WHERE 子句)使用 plpgsql 会产生相当大的负面影响。

  2. 您可以重写您的函数以返回 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)
    
  3. 如果 a_functionb_function 的结果不依赖于内容 ab ,然后可以通过在这些函数上设置标志 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/

相关文章:

postgresql - 如何在 PL/pgSQL 中将 SELECT 嵌套到 UPDATE 语句中

sql - 设置变量时,记录的 IS NOT NULL 测试不返回 TRUE

c# - .NET MySqlTransactions 是否在本地进行批处理/缓冲?

php - 为字符串匹配优化的数据库/数据源?

database-design - 设计一个高性能的酒店房间预订系统

sql - PostgreSQL 中的 GROUP BY 和 COUNT

postgresql - 将 json 字符串从 spring 应用程序插入到 postgres jsonb

java - 将应用程序连接到 postgres 数据库

python - 将 postgres 作为 Docker 服务运行时为 "psql: fe_sendauth: no password supplied"

postgresql - 在表中保留一定数量的记录