OFFSET
lớn sẽ luôn luôn chậm. Postgres phải sắp xếp tất cả các hàng và đếm số có thể nhìn thấy những cái cho đến bù đắp của bạn. Để bỏ qua tất cả các hàng trước đó trực tiếp bạn có thể thêm một row_number
được lập chỉ mục vào bảng (hoặc tạo MATERIALIZED VIEW
bao gồm row_number
đã nói ) và làm việc với WHERE row_number > x
thay vì OFFSET x
.
Tuy nhiên, cách tiếp cận này chỉ hợp lý đối với dữ liệu chỉ đọc (hoặc phần lớn). Triển khai tương tự cho dữ liệu bảng có thể thay đổi đồng thời là thách thức hơn. Bạn cần bắt đầu bằng cách xác định hành vi mong muốn chính xác .
Tôi đề xuất một cách tiếp cận khác cho phân trang :
SELECT *
FROM big_table
WHERE (vote, id) > (vote_x, id_x) -- ROW values
ORDER BY vote, id -- needs to be deterministic
LIMIT n;
Ở đâu vote_x
và id_x
là từ cuối cùng hàng của trang trước (cho cả DESC
và ASC
). Hoặc từ đầu tiên nếu điều hướng ngược lại .
So sánh các giá trị hàng được hỗ trợ bởi chỉ mục bạn đã có - một tính năng tuân thủ tiêu chuẩn ISO SQL, nhưng không phải mọi RDBMS đều hỗ trợ nó.
CREATE INDEX vote_order_asc ON big_table (vote, id);
Hoặc đối với thứ tự giảm dần:
SELECT *
FROM big_table
WHERE (vote, id) < (vote_x, id_x) -- ROW values
ORDER BY vote DESC, id DESC
LIMIT n;
Có thể sử dụng cùng một chỉ mục.
Tôi khuyên bạn nên khai báo các cột của mình NOT NULL
hoặc tự làm quen với NULLS FIRST|LAST
cấu tạo:
- PostgreSQL sắp xếp theo datetime asc, null trước?
Lưu ý hai điều cụ thể:
-
ROW
các giá trị trongWHERE
mệnh đề không thể được thay thế bằng các trường thành viên riêng biệt.WHERE (vote, id) > (vote_x, id_x)
không thể được thay thế bằng:WHERE vote >= vote_x AND id > id_xĐiều đó sẽ loại trừ tất cả các hàng có
id <= id_x
, trong khi chúng tôi chỉ muốn làm điều đó cho cùng một phiếu bầu chứ không phải cho lần tiếp theo. Bản dịch chính xác sẽ là:WHERE (vote = vote_x AND id > id_x) OR vote > vote_x
... không phù hợp với các chỉ mục và ngày càng trở nên phức tạp đối với nhiều cột hơn.
Sẽ đơn giản cho một single cột, rõ ràng. Đó là trường hợp đặc biệt mà tôi đã đề cập ngay từ đầu.
-
Kỹ thuật này không hoạt động đối với các hướng hỗn hợp trong
ORDER BY
như:ORDER BY vote ASC, id DESC
Ít nhất thì tôi không thể nghĩ ra một chung chung cách để thực hiện điều này một cách hiệu quả. Nếu ít nhất một trong cả hai cột là kiểu số, bạn có thể sử dụng chỉ mục chức năng có giá trị đảo ngược trên
(vote, (id * -1))
- và sử dụng cùng một biểu thức trongORDER BY
:ORDER BY vote ASC, (id * -1) ASC
Có liên quan:
- Thuật ngữ cú pháp SQL cho 'WHERE (col1, col2) <(val1, val2)'
- Cải thiện hiệu suất cho thứ tự bằng các cột từ nhiều bảng
Đặc biệt lưu ý bài thuyết trình của Markus Wina và tôi đã liên kết đến:
- "Việc phân trang được thực hiện theo cách PostgreSQL"