Mit PostgreSQL (8.4), ich bin eine Ansicht erstellen , die verschiedene Ergebnisse von einigen Tabellen zusammenfasst (zB Erstellen von Spalten a
, b
, c
in der Ansicht), und dann brauche ich einige dieser Ergebnisse zusammen in derselben Abfrage zu kombinieren (zB a+b
, a-b
, (a+b)/c
, ...), um die Endergebnisse zu erzielen. Was mir auffällt, ist, dass die Zwischenergebnisse bei jeder Verwendung vollständig berechnet werden, selbst wenn dies innerhalb derselben Abfrage erfolgt.
Gibt es eine Möglichkeit, dies zu optimieren, um zu vermeiden, dass jedes Mal dieselben Ergebnisse berechnet werden?
Hier ist ein vereinfachtes Beispiel, das das Problem reproduziert.
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;
Hier ist eine Ansicht, die die zeitaufwändigsten Vorgänge ausführt:
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
erstellt diesen Plan (viaEXPLAIN 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
erstellt diesen Plan :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
Hier a, a
dauert die Auswahl doppelt so lange wie die Auswahl a
, während sie wirklich nur einmal berechnet werden können. Mit wird beispielsweise SELECT a, a+b, a-b FROM testview1
der Unterplan a
dreimal und b
zweimal durchlaufen , während die Ausführungszeit auf 2/5 der Gesamtzeit reduziert werden könnte (vorausgesetzt, + und - sind hier vernachlässigbar).
Es ist gut, dass nicht verwendete Spalten ( b
und c
) nicht berechnet werden, wenn sie nicht benötigt werden. Gibt es jedoch eine Möglichkeit, dieselben verwendeten Spalten nur einmal aus der Ansicht zu berechnen?
BEARBEITEN : @Frank Heikens schlug korrekt vor, einen Index zu verwenden, der im obigen Beispiel fehlte. Dies verbessert zwar die Geschwindigkeit für jeden Unterplan, verhindert jedoch nicht, dass dieselbe Unterabfrage mehrmals berechnet wird. Entschuldigung, ich hätte dies in die ursprüngliche Frage aufnehmen sollen, um es klar zu machen.
quelle
Sie benötigen einen Index für test1_id in Tabelle test2, der die Dinge ändert.
quelle
SELECT a, a, a, a, a FROM testview1
dauert immer noch fünfmal länger alsSELECT a FROM testview1
.