Postgresql COALESCE проблема производительности - PullRequest
6 голосов
/ 21 июня 2011

У меня есть эта таблица в 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 = ?

Есть ли какое-либо решение, кроме оптимизации строки запроса на уровне приложения?

РЕДАКТИРОВАТЬ: на практике вопрос заключается в том, как переписать запрос select * from my_table where id=coalesce(?, id) and value=? иметь наихудшую производительность не ниже, чем select * from my_table where value=? в Postgresql 9.0

Ответы [ 2 ]

3 голосов
/ 23 июня 2011

Попробуйте переписать запрос вида

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
2 голосов
/ 21 июня 2011

После создания аналогичной таблицы, ее заполнения, обновления статистики и, наконец, просмотра выходных данных 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

Так что я бы сказал, что это медленнее, потому что движок БД а) оценивает выражение COALESCE (), а не оптимизирует его, и б) оценка его включает в себя дополнительное условие фильтра.

...