Đối với chỉ 400 trạm, truy vấn này sẽ ồ ạt nhanh hơn:
SELECT s.station_id, l.submitted_at, l.level_sensor
FROM station s
CROSS JOIN LATERAL (
SELECT submitted_at, level_sensor
FROM station_logs
WHERE station_id = s.station_id
ORDER BY submitted_at DESC NULLS LAST
LIMIT 1
) l;
dbfiddle ở đây
(so sánh các kế hoạch cho truy vấn này, thay thế của Abelisto và bản gốc của bạn)
Kết quả EXPLAIN ANALYZE
theo quy định của OP:
Vòng lặp lồng nhau (chi phí = 0,56..356,65 hàng = 102 chiều rộng = 20) (thời gian thực tế = 0,034..0.979 hàng = 98 vòng lặp = 1)
-> Seq Quét trên các trạm s (chi phí = 0,00..3,02 hàng = 102 chiều rộng = 4) (thời gian thực tế = 0,009..0.016 hàng = 102 vòng lặp = 1)
-> Giới hạn (chi phí = 0,56..3,45 hàng = 1 chiều rộng = 16) (thời gian thực tế = 0,009..0.009 hàng = 1 vòng lặp = 102)
-> Quét chỉ mục bằng cách sử dụng trạm_id__submit_at trên trạm_logs (chi phí = 0,56..664062,38 hàng = 230223 chiều rộng = 16) (thời gian thực tế = 0,009 $
Chỉ số Cond: (trạm_id = s.id)
Thời gian lập kế hoạch: 0,542 ms
Thời gian thực hiện: 1.013 ms - !!
Chỉ mục duy nhất bạn cần là chỉ số bạn đã tạo : station_id__submitted_at
. Các UNIQUE
ràng buộc uniq_sid_sat
cũng làm công việc, về cơ bản. Duy trì cả hai dường như lãng phí không gian đĩa và ghi hiệu suất.
Tôi đã thêm NULLS LAST
vào ORDER BY
trong truy vấn vì submitted_at
không được xác định NOT NULL
. Lý tưởng nhất, nếu có thể!, Thêm một NOT NULL
ràng buộc vào cột submitted_at
, bỏ chỉ mục bổ sung và xóa NULLS LAST
khỏi truy vấn.
Nếu submitted_at
có thể NULL
, hãy tạo UNIQUE
chỉ mục này để thay thế cả chỉ mục hiện tại và ràng buộc duy nhất của bạn:
CREATE UNIQUE INDEX station_logs_uni ON station_logs(station_id, submitted_at DESC NULLS LAST);
Xem xét:
Đây là giả sử một bảng riêng biệtstation
với một hàng cho mỗi hàng có liên quan station_id
(thường là PK) - mà bạn nên có một trong hai cách. Nếu bạn không có nó, hãy tạo nó. Một lần nữa, rất nhanh với kỹ thuật rCTE này:
CREATE TABLE station AS
WITH RECURSIVE cte AS (
(
SELECT station_id
FROM station_logs
ORDER BY station_id
LIMIT 1
)
UNION ALL
SELECT l.station_id
FROM cte c
, LATERAL (
SELECT station_id
FROM station_logs
WHERE station_id > c.station_id
ORDER BY station_id
LIMIT 1
) l
)
TABLE cte;
Tôi sử dụng nó trong fiddle là tốt. Bạn có thể sử dụng một truy vấn tương tự để giải quyết trực tiếp nhiệm vụ của mình mà không cần station
bảng - nếu bạn không thể bị thuyết phục để tạo ra nó.
Hướng dẫn chi tiết, giải thích và giải pháp thay thế:
Tối ưu hóa chỉ số
Truy vấn của bạn nên rất nhanh bây giờ. Chỉ khi bạn vẫn cần tối ưu hóa hiệu suất đọc ...
Có thể có ý nghĩa khi thêm level_sensor
cột cuối cùng vào chỉ mục để cho phép quét chỉ mục , như joanolo đã nhận xét .
Con: Nó làm cho chỉ mục lớn hơn - làm tăng thêm một ít chi phí cho tất cả các truy vấn sử dụng nó.
Pro: Nếu bạn thực sự nhận được chỉ mục quét nó, truy vấn trong tay hoàn toàn không phải truy cập các trang heap, điều này làm cho nó nhanh gấp đôi. Nhưng đó có thể là một lợi ích không đáng kể cho truy vấn rất nhanh bây giờ.
Tuy nhiên , tôi không mong đợi rằng sẽ làm việc cho trường hợp của bạn. Bạn đã đề cập:
... khoảng 20k hàng mỗi ngày station_id
.
Thông thường, điều đó cho thấy tải ghi không ngừng ( station_id
cứ sau 5 giây). Và bạn quan tâm đến hàng mới nhất . Quét chỉ mục chỉ hoạt động đối với các trang heap hiển thị cho tất cả các giao dịch (bit trong bản đồ hiển thị được đặt). Bạn sẽ phải chạy các VACUUM
cài đặt cực kỳ tích cực cho bảng để theo kịp tải ghi và nó vẫn không hoạt động hầu hết thời gian. Nếu các giả định của tôi là chính xác, quét chỉ mục sẽ bị loại bỏ, đừng thêm level_sensor
vào chỉ mục.
OTOH, nếu các giả định của tôi giữ và bảng của bạn đang phát triển rất lớn , chỉ số BRIN có thể giúp ích. Liên quan:
Hoặc, thậm chí chuyên biệt hơn và hiệu quả hơn: Một chỉ mục một phần chỉ cho các bổ sung mới nhất để cắt bỏ phần lớn các hàng không liên quan:
CREATE INDEX station_id__submitted_at_recent_idx ON station_logs(station_id, submitted_at DESC NULLS LAST)
WHERE submitted_at > '2017-06-24 00:00';
Chọn dấu thời gian mà bạn biết rằng các hàng trẻ hơn phải tồn tại. Bạn phải thêm một WHERE
điều kiện phù hợp cho tất cả các truy vấn, như:
...
WHERE station_id = s.station_id
AND submitted_at > '2017-06-24 00:00'
...
Bạn phải điều chỉnh chỉ mục và truy vấn theo thời gian.
Câu trả lời liên quan với nhiều chi tiết hơn: