PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Tối ưu hóa truy vấn với OFFSET trên bảng lớn

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_xid_x là từ cuối cùng hàng của trang trước (cho cả DESCASC ). 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ể:

  1. ROW các giá trị trong WHERE 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.

  2. 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 trong ORDER 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"


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. GroupingError:ERROR:cột phải xuất hiện trong mệnh đề GROUP BY hoặc được sử dụng trong một hàm tổng hợp

  2. Chuyển đổi loại. Tôi phải làm gì với giá trị PostgreSQL OID trong libpq trong C?

  3. 3 cách liệt kê tất cả các thủ tục được lưu trữ tham chiếu đến một bảng trong PostgreSQL

  4. Thiết lập và bảo trì bản sao PostgreSQL bằng Ansible

  5. Chỉ mục trên Dấu thời gian:Các hàm trong biểu thức chỉ mục phải được đánh dấu là CÓ THỂ NGAY LẬP TỨC