Postgresql COALESCE 性能问题

11

我有一个在PostgreSQL中的表:

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

我的表中有大约50000行。

问题是,为什么这个查询语句:

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

比这个慢:

SELECT * FROM my_table WHERE value = ?

除了在应用层优化查询字符串之外,还有其他解决方案吗?

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


3
你的查询很奇怪。id = COALESCE(null, id) 的目的是什么?由于 id 被定义为 NOT NULL,而 id = id 总是返回 true,因此 COALESCE 将始终返回 id。请问需要翻译哪些内容? - Steve Prentice
@steve,“select .... id=id ...”也很慢。你有什么想法为什么会这样? - Tair
4
我认为优化器会加入休眠以使你困惑,因为你给它提供了恼人/无用的诡计查询。 - fvu
我的猜测是该值上有索引,而不是ID。只是猜测。 - Steve Prentice
1
@fvu 实际语句是 select * from my_table where id=coalesce(?, id) and value=?,虽然有点烦人,但并不完全无用 :) - Tair
@tair,这样就有意义多了... - fvu
2个回答

6
尝试重新编写表单的查询
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

5
从创建类似的表格、填充数据、更新统计信息,到最后查看EXPLAIN ANALYZE的输出结果,唯一的区别在于第一个查询会进行如下筛选:
Filter: ((id = COALESCE(id)) AND (value = 3))

第二个过滤器的功能如下:
Filter: (value = 3)

如果在“value”列上建立索引,我会看到明显不同的性能和执行计划。 在第一种情况下

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

我认为它较慢是因为数据库引擎 a) 评估 COALESCE() 表达式而不是优化掉它,b) 评估它涉及一个额外的过滤条件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接