Menggunakan PostgreSQL (8,4), saya menciptakan pandangan yang merangkum berbagai hasil dari beberapa tabel (misalnya membuat kolom a
, b
, c
dalam tampilan), dan kemudian saya harus menggabungkan beberapa hasil ini bersama-sama dalam query yang sama (misalnya a+b
, a-b
, (a+b)/c
, ...), sehingga menghasilkan hasil akhir. Yang saya perhatikan adalah bahwa hasil antara sepenuhnya dihitung setiap kali digunakan, bahkan jika itu dilakukan dalam permintaan yang sama.
Apakah ada cara untuk mengoptimalkan ini untuk menghindari hasil yang sama yang akan dihitung setiap waktu?
Berikut adalah contoh sederhana yang mereproduksi masalah.
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;
Berikut ini adalah tampilan yang melakukan operasi yang paling memakan waktu:
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
menghasilkan rencana ini (melaluiEXPLAIN 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
menghasilkan rencana ini :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
Di sini, memilih a, a
memakan waktu dua kali lebih lama daripada memilih a
, sedangkan mereka benar-benar dapat dihitung hanya sekali. Sebagai contoh, dengan SELECT a, a+b, a-b FROM testview1
, melewati sub-rencana untuk a
3 kali dan melalui b
dua kali, sedangkan waktu eksekusi dapat dikurangi menjadi 2/5 dari total waktu (dengan asumsi + dan - dapat diabaikan di sini).
Ini adalah hal yang baik karena tidak menghitung kolom yang tidak digunakan ( b
dan c
) ketika tidak diperlukan, tetapi apakah ada cara untuk membuatnya menghitung kolom yang digunakan sama dari tampilan hanya sekali?
EDIT: @ Frank Heikens dengan benar menyarankan untuk menggunakan indeks, yang tidak ada dalam contoh di atas. Meskipun meningkatkan kecepatan untuk setiap sub-paket, itu tidak mencegah sub-query yang sama untuk dihitung berulang kali. Maaf, saya harus meletakkan ini di pertanyaan awal untuk membuatnya lebih jelas.