Использование PostgreSQL (8.4), я создаю представление , в котором обобщаются различные результаты нескольких таблиц (например , создание колонн a
, b
, c
в представлении), а затем мне нужно объединить некоторые из этих результатов вместе в одном запросе (например a+b
, a-b
, (a+b)/c
...), чтобы получить окончательные результаты. Я замечаю, что промежуточные результаты полностью вычисляются при каждом их использовании, даже если это делается в рамках одного и того же запроса.
Есть ли способ оптимизировать это, чтобы избежать того же результата, который будет вычисляться каждый раз?
Вот упрощенный пример, который воспроизводит проблему.
CREATE TABLE test1 (
id SERIAL PRIMARY KEY,
log_timestamp TIMESTAMP NOT NULL
);
CREATE TABLE test2 (
test1_id INTEGER NOT NULL REFERENCES test1(id),
category VARCHAR(10) NOT NULL,
col1 INTEGER,
col2 INTEGER
);
CREATE INDEX test_category_idx ON test2(category);
-- Added after edit to this question
CREATE INDEX test_id_idx ON test2(test1_id);
-- Populating with test data.
INSERT INTO test1(log_timestamp)
SELECT * FROM generate_series('2011-01-01'::timestamp, '2012-01-01'::timestamp, '1 hour');
INSERT INTO test2
SELECT id, substr(upper(md5(random()::TEXT)), 1, 1),
(20000*random()-10000)::int, (3000*random()-200)::int FROM test1;
INSERT INTO test2
SELECT id, substr(upper(md5(random()::TEXT)), 1, 1),
(2000*random()-1000)::int, (3000*random()-200)::int FROM test1;
INSERT INTO test2
SELECT id, substr(upper(md5(random()::TEXT)), 1, 1),
(2000*random()-40)::int, (3000*random()-200)::int FROM test1;
Вот представление, которое выполняет самые трудоемкие операции:
CREATE VIEW testview1 AS
SELECT
t1.id,
t1.log_timestamp,
(SELECT SUM(t2.col1) FROM test2 t2 WHERE t2.test1_id=t1.id AND category='A') AS a,
(SELECT SUM(t2.col2) FROM test2 t2 WHERE t2.test1_id=t1.id AND category='B') AS b,
(SELECT SUM(t2.col1 - t2.col2) FROM test2 t2 WHERE t2.test1_id=t1.id AND category='C') AS c
FROM test1 t1;
SELECT a FROM testview1
производит этот план (черезEXPLAIN ANALYZE
):Seq Scan on test1 t1 (cost=0.00..1787086.55 rows=8761 width=4) (actual time=12.877..10517.575 rows=8761 loops=1) SubPlan 1 -> Aggregate (cost=203.96..203.97 rows=1 width=4) (actual time=1.193..1.193 rows=1 loops=8761) -> Bitmap Heap Scan on test2 t2 (cost=36.49..203.95 rows=1 width=4) (actual time=1.109..1.177 rows=0 loops=8761) Recheck Cond: ((category)::text = 'A'::text) Filter: (test1_id = $0) -> Bitmap Index Scan on test_category_idx (cost=0.00..36.49 rows=1631 width=0) (actual time=0.414..0.414 rows=1631 loops=8761) Index Cond: ((category)::text = 'A'::text) Total runtime: 10522.346 ms
SELECT a, a FROM testview1
производит этот план :Seq Scan on test1 t1 (cost=0.00..3574037.50 rows=8761 width=4) (actual time=3.343..20550.817 rows=8761 loops=1) SubPlan 1 -> Aggregate (cost=203.96..203.97 rows=1 width=4) (actual time=1.183..1.183 rows=1 loops=8761) -> Bitmap Heap Scan on test2 t2 (cost=36.49..203.95 rows=1 width=4) (actual time=1.100..1.166 rows=0 loops=8761) Recheck Cond: ((category)::text = 'A'::text) Filter: (test1_id = $0) -> Bitmap Index Scan on test_category_idx (cost=0.00..36.49 rows=1631 width=0) (actual time=0.418..0.418 rows=1631 loops=8761) Index Cond: ((category)::text = 'A'::text) SubPlan 2 -> Aggregate (cost=203.96..203.97 rows=1 width=4) (actual time=1.154..1.154 rows=1 loops=8761) -> Bitmap Heap Scan on test2 t2 (cost=36.49..203.95 rows=1 width=4) (actual time=1.083..1.143 rows=0 loops=8761) Recheck Cond: ((category)::text = 'A'::text) Filter: (test1_id = $0) -> Bitmap Index Scan on test_category_idx (cost=0.00..36.49 rows=1631 width=0) (actual time=0.426..0.426 rows=1631 loops=8761) Index Cond: ((category)::text = 'A'::text) Total runtime: 20557.581 ms
Здесь выбор a, a
занимает в два раза больше времени, чем выбор a
, тогда как на самом деле их можно вычислить только один раз. Например, с помощью SELECT a, a+b, a-b FROM testview1
он проходит подплан a
3 раза и b
два раза, тогда как время выполнения может быть сокращено до 2/5 от общего времени (при условии, что + и - здесь незначительны).
Хорошо, что он не вычисляет неиспользуемые столбцы ( b
и c
), когда они не нужны, но есть ли способ заставить его вычислять те же самые используемые столбцы из представления только один раз?
РЕДАКТИРОВАТЬ: @Frank Heikens правильно предложил использовать индекс, который отсутствовал в примере выше. Несмотря на то, что он улучшает скорость для каждого подплана, он не мешает вычислению одного и того же подзапроса несколько раз. Извините, я должен был поставить это в первоначальный вопрос, чтобы прояснить.