Sử dụng PostgreSQL (8.4), tôi là tạo ra một cái nhìn tóm tắt kết quả khác nhau từ một vài bảng (ví dụ như việc tạo ra các cột a
, b
, c
trong giao diện), và sau đó tôi cần phải kết hợp một số trong những kết quả với nhau trong cùng một truy vấn (ví dụ a+b
, a-b
, (a+b)/c
, ...), để tạo ra kết quả cuối cùng. Điều tôi nhận thấy là các kết quả trung gian được tính toán đầy đủ mỗi lần chúng được sử dụng, ngay cả khi nó được thực hiện trong cùng một truy vấn.
Có cách nào để tối ưu hóa điều này để tránh những kết quả tương tự được tính toán mỗi lần không?
Dưới đây là một ví dụ đơn giản tái tạo vấn đề.
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;
Đây là một khung nhìn thực hiện các hoạt động tốn nhiều thời gian nhất:
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
tạo kế hoạch này (thông quaEXPLAIN 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
sản xuất kế hoạch này :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
Ở đây, việc chọn a, a
mất gấp đôi thời gian chọn a
, trong khi chúng thực sự có thể được tính chỉ một lần. Ví dụ, với SELECT a, a+b, a-b FROM testview1
, nó đi qua kế hoạch phụ trong a
3 lần và qua b
hai lần, trong khi đó thời gian thực hiện có thể giảm xuống còn 2/5 tổng thời gian (giả sử + và - không đáng kể ở đây).
Thật tốt khi nó không tính toán các cột không sử dụng ( b
và c
) khi chúng không cần thiết, nhưng có cách nào để làm cho nó tính toán các cột được sử dụng tương tự từ chế độ xem chỉ một lần không?
EDIT: @Frank Heikens đã đề xuất chính xác để sử dụng một chỉ mục bị thiếu trong ví dụ trên. Mặc dù nó cải thiện tốc độ cho từng kế hoạch phụ, nhưng nó không ngăn được cùng một truy vấn phụ được tính toán nhiều lần. Xin lỗi, tôi nên đặt điều này trong câu hỏi ban đầu để làm cho nó rõ ràng.