Giả sử ít nhất Postgres 9.3.
Chỉ mục
Đầu tiên, chỉ mục đa cột sẽ giúp:
CREATE INDEX observations_special_idx
ON observations(station_id, created_at DESC, id)
created_at DESC
phù hợp hơn một chút, nhưng chỉ mục sẽ vẫn được quét ngược với tốc độ gần như tương tự mà không có DESC
.
Giả sử created_at
được định nghĩa NOT NULL
, nếu không, hãy xem xét DESC NULLS LAST
trong chỉ mục và truy vấn:
- PostgreSQL sắp xếp theo datetime asc, null trước?
Cột cuối cùng id
chỉ hữu ích nếu bạn nhận được một bản quét chỉ chỉ mục từ nó, có thể sẽ không hoạt động nếu bạn thêm nhiều hàng mới liên tục. Trong trường hợp này, hãy xóa id
từ chỉ mục.
Truy vấn đơn giản hơn (vẫn chậm)
Đơn giản hóa truy vấn của bạn, lựa chọn con bên trong không giúp ích gì:
SELECT id
FROM (
SELECT station_id, id, created_at
, row_number() OVER (PARTITION BY station_id
ORDER BY created_at DESC) AS rn
FROM observations
) s
WHERE rn <= #{n} -- your limit here
ORDER BY station_id, created_at DESC;
Sẽ nhanh hơn một chút, nhưng vẫn chậm.
Truy vấn nhanh
- Giả sử bạn có tương đối ít trạm và tương đối nhiều quan sát trên mỗi trạm.
- Cũng giả sử
station_id
id được định nghĩa làNOT NULL
.
Để trở thành thực sự nhanh chóng, bạn cần tương đương với quét chỉ mục lỏng lẻo (chưa được triển khai trong Postgres). Câu trả lời liên quan:
- Tối ưu hóa truy vấn GROUP BY để truy xuất bản ghi mới nhất cho mỗi người dùng
Nếu bạn có một bảng stations
riêng biệt (có vẻ như có thể xảy ra), bạn có thể mô phỏng điều này bằng JOIN LATERAL
(Postgres 9.3+):
SELECT o.id
FROM stations s
CROSS JOIN LATERAL (
SELECT o.id
FROM observations o
WHERE o.station_id = s.station_id -- lateral reference
ORDER BY o.created_at DESC
LIMIT #{n} -- your limit here
) o
ORDER BY s.station_id, o.created_at DESC;
Nếu bạn không có bảng stations
, điều tốt nhất tiếp theo là tạo và duy trì một. Có thể thêm một tham chiếu khóa ngoại để thực thi tính toàn vẹn quan hệ.
Nếu đó không phải là một lựa chọn, bạn có thể chưng cất một bảng như vậy một cách nhanh chóng. Các tùy chọn đơn giản sẽ là:
SELECT DISTINCT station_id FROM observations;
SELECT station_id FROM observations GROUP BY 1;
Nhưng một trong hai sẽ cần quét tuần tự và chậm. Làm cho Postgres sử dụng chỉ mục trên (hoặc bất kỳ chỉ mục btree nào với station_id
làm cột hàng đầu) với CTE đệ quy :
WITH RECURSIVE stations AS (
( -- extra pair of parentheses ...
SELECT station_id
FROM observations
ORDER BY station_id
LIMIT 1
) -- ... is required!
UNION ALL
SELECT (SELECT o.station_id
FROM observations o
WHERE o.station_id > s.station_id
ORDER BY o.station_id
LIMIT 1)
FROM stations s
WHERE s.station_id IS NOT NULL -- serves as break condition
)
SELECT station_id
FROM stations
WHERE station_id IS NOT NULL; -- remove dangling row with NULL
Sử dụng nó làm thay thế thả vào cho stations
bảng trong truy vấn đơn giản ở trên:
WITH RECURSIVE stations AS (
(
SELECT station_id
FROM observations
ORDER BY station_id
LIMIT 1
)
UNION ALL
SELECT (SELECT o.station_id
FROM observations o
WHERE o.station_id > s.station_id
ORDER BY o.station_id
LIMIT 1)
FROM stations s
WHERE s.station_id IS NOT NULL
)
SELECT o.id
FROM stations s
CROSS JOIN LATERAL (
SELECT o.id, o.created_at
FROM observations o
WHERE o.station_id = s.station_id
ORDER BY o.created_at DESC
LIMIT #{n} -- your limit here
) o
WHERE s.station_id IS NOT NULL
ORDER BY s.station_id, o.created_at DESC;
Điều này vẫn sẽ nhanh hơn so với những gì bạn có theo thứ tự cường độ .
SQL Fiddle tại đây (9.6)
db <> fiddle tại đây