Postgresql COALESCE 性能问题

Posted

技术标签:

【中文标题】Postgresql COALESCE 性能问题【英文标题】:Postgresql COALESCE performance problem 【发布时间】:2011-09-19 13:33:51 【问题描述】:

我在 Postgresql 中有这张表:

CREATE TABLE my_table
(
    id bigint NOT NULL,
    value bigint,
    CONSTRAINT my_table_pkey PRIMARY KEY (id)
);

my_table 中有 ~50000 行。

问题是,为什么要查询:

SELECT * FROM my_table WHERE id = COALESCE(null, id) and value = ?

比这个慢:

SELECT * FROM my_table WHERE value = ?

除了在app-layer优化查询字符串,有什么解决办法吗?

编辑:实际上,问题是如何重写查询 select * from my_table where id=coalesce(?, id) and value=? 以使最坏情况下的性能不低于 Postgresql 9.0 中 select * from my_table where value=? 的性能

【问题讨论】:

您的查询很奇怪。 id = COALESCE(null, id) 的目的是什么? COALESCE 将始终返回 id,因为 id 被定义为 NOT NULL 并且 id = id 也将始终返回 true。 @steve select .... id=id ... 也很慢。知道为什么吗? @tair 我认为优化器包含让你困惑的睡眠,因为你给它提供了烦人/无用的技巧查询。 我的猜测是值而不是 id 上有索引。不过只是猜测。 @fvu 实际的声明是select * from my_table where id=coalesce(?, id) and value=?,所以虽然很烦人,但它并非完全没用:) 【参考方案1】:

从创建一个类似的表、填充它、更新统计信息到最后查看EXPLAIN ANALYZE 的输出,我看到的唯一区别是第一个查询过滤器是这样的:

Filter: ((id = COALESCE(id)) AND (value = 3))

第二个过滤器是这样的:

Filter: (value = 3)

当“值”列上有索引时,我发现性能和执行计划大不相同。第一种情况

Bitmap Heap Scan on my_table  (cost=19.52..552.60 rows=5 width=16) (actual time=19.311..20.679 rows=1000 loops=1)
  Recheck Cond: (value = 3)
  Filter: (id = COALESCE(id))
  ->  Bitmap Index Scan on t2  (cost=0.00..19.52 rows=968 width=0) (actual time=19.260..19.260 rows=1000 loops=1)
        Index Cond: (value = 3)
Total runtime: 22.138 ms

在第二个

Bitmap Heap Scan on my_table  (cost=19.76..550.42 rows=968 width=16) (actual time=0.302..1.293 rows=1000 loops=1)
  Recheck Cond: (value = 3)
  ->  Bitmap Index Scan on t2  (cost=0.00..19.52 rows=968 width=0) (actual time=0.276..0.276 rows=1000 loops=1)
        Index Cond: (value = 3)
Total runtime: 2.174 ms

所以我会说它更慢,因为 db 引擎 a) 评估 COALESCE() 表达式而不是优化它,并且 b) 评估它涉及一个额外的过滤条件。

【讨论】:

【参考方案2】:

尝试重写表单的查询

SELECT *
  FROM my_table
 WHERE value = ?
   AND (? IS NULL OR id = ?)

来自我自己的快速测试

INSERT INTO my_table select generate_series(1,50000),1;
UPDATE my_table SET value = id%17;

CREATE INDEX val_idx ON my_table(value);

VACUUM ANALYZE my_table;

\set idval 17
\set pval   0

explain analyze 
SELECT *
  FROM my_table
 WHERE value = :pval
   AND (:idval IS NULL OR id = :idval);

Index Scan using my_table_pkey on my_table  (cost=0.00..8.29 rows=1 width=16) (actual time=0.034..0.035 rows=1 loops=1)
   Index Cond: (id = 17)
   Filter: (value = 0)
 Total runtime: 0.064 ms

\set idval null

explain analyze 
SELECT *
  FROM my_table
 WHERE value = :pval
   AND (:idval IS NULL OR id = :idval);

Bitmap Heap Scan on my_table  (cost=58.59..635.62 rows=2882 width=16) (actual time=0.373..1.594 rows=2941 loops=1)
   Recheck Cond: (value = 0)
   ->  Bitmap Index Scan on validx  (cost=0.00..57.87 rows=2882 width=0) (actual time=0.324..0.324 rows=2941 loops=1)
         Index Cond: (value = 0)
 Total runtime: 1.811 ms

【讨论】:

以上是关于Postgresql COALESCE 性能问题的主要内容,如果未能解决你的问题,请参考以下文章

在 PostgreSQL 中使用 COALESCE 处理 NULL 值

PostgreSQL判断是否为空coalesce

Postgresql使用coalesce实现类似oracle的NVL方法

Postgresql 到 Laravel 雄辩

MySQL 的 WHERE 子句中的 CASE 或 COALESCE 性能

避免在 PostgreSQL 中被零除