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

Nhận các hàng được phân trang và tổng số trong một truy vấn

Điều đầu tiên trước tiên:bạn có thể sử dụng kết quả từ một CTE nhiều lần trong cùng một truy vấn, đó là tính năng chính của của CTE .) Những gì bạn có sẽ hoạt động như thế này (trong khi vẫn sử dụng CTE một lần duy nhất):

WITH cte AS (
   SELECT * FROM (
      SELECT *, row_number()  -- see below
                OVER (PARTITION BY person_id
                      ORDER BY submission_date DESC NULLS LAST  -- see below
                             , last_updated DESC NULLS LAST  -- see below
                             , id DESC) AS rn
      FROM  tbl
      ) sub
   WHERE  rn = 1
   AND    status IN ('ACCEPTED', 'CORRECTED')
   )
SELECT *, count(*) OVER () AS total_rows_in_cte
FROM   cte
LIMIT  10
OFFSET 0;  -- see below

Báo trước 1:rank()

rank() có thể trả về nhiều hàng cho mỗi person_id với rank = 1 . DISTINCT ON (person_id) (như Gordon đã cung cấp) là sự thay thế có thể áp dụng cho row_number() - cái nào phù hợp với bạn, vì thông tin bổ sung được làm rõ. Xem:

Báo trước 2:ORDER BY submission_date DESC

Cả submission_date cũng không phải last_updated được định nghĩa NOT NULL . Có thể là sự cố với ORDER BY submission_date DESC, last_updated DESC ... Xem:

Các cột đó có thực sự là NOT NULL không ?

Bạn đã trả lời:

Chuỗi trống không được phép cho loại date . Giữ cho các cột có giá trị rỗng. NULL là giá trị thích hợp cho những trường hợp đó. Sử dụng NULLS LAST như đã trình bày để tránh NULL được sắp xếp trên cùng.

Báo trước 3:OFFSET

Nếu OFFSET bằng hoặc lớn hơn số hàng được trả về bởi CTE, bạn nhận được không có hàng , vì vậy cũng không có tổng số. Xem:

Giải pháp tạm thời

Giải quyết tất cả các cảnh báo cho đến nay và dựa trên thông tin bổ sung, chúng tôi có thể đi đến truy vấn sau:

WITH cte AS (
   SELECT DISTINCT ON (person_id) *
   FROM   tbl
   WHERE  status IN ('ACCEPTED', 'CORRECTED')
   ORDER  BY person_id, submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC
   )
SELECT *
FROM  (
   TABLE  cte
   ORDER  BY person_id  -- ?? see below
   LIMIT  10
   OFFSET 0
   ) sub
RIGHT  JOIN (SELECT count(*) FROM cte) c(total_rows_in_cte) ON true;

Bây giờ CTE thực sự là được sử dụng hai lần. RIGHT JOIN đảm bảo rằng chúng tôi nhận được tổng số, bất kể OFFSET . DISTINCT ON nên thực hiện OK-ish cho một vài hàng duy nhất trên mỗi (person_id) trong truy vấn cơ sở.

Nhưng bạn có hàng rộng. Chiều rộng trung bình là bao nhiêu? Truy vấn có thể sẽ dẫn đến việc quét tuần tự trên toàn bộ bảng. Chỉ mục sẽ không giúp được gì (nhiều). Tất cả những điều này sẽ vẫn cực kỳ kém hiệu quả cho việc phân trang . Xem:

Bạn không thể liên quan đến một chỉ mục để phân trang vì nó dựa trên bảng dẫn xuất từ ​​CTE. Và tiêu chí sắp xếp thực tế của bạn cho phân trang vẫn chưa rõ ràng (ORDER BY id ?). Nếu phân trang là mục tiêu, bạn rất cần một kiểu truy vấn khác. Nếu bạn chỉ quan tâm đến một vài trang đầu tiên, bạn cần một kiểu truy vấn khác. Giải pháp tốt nhất phụ thuộc vào thông tin còn thiếu trong câu hỏi ...

Nhanh hơn hoàn toàn

Đối với mục tiêu cập nhật của bạn:

(Bỏ qua "cho các tiêu chí, loại, kế hoạch, trạng thái bộ lọc được chỉ định" vì sự đơn giản.)

Và:

Dựa trên hai chỉ số chuyên biệt này :

CREATE INDEX ON tbl (submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST)
WHERE  status IN ('ACCEPTED', 'CORRECTED'); -- optional

CREATE INDEX ON tbl (person_id, submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST);

Chạy truy vấn này:

WITH RECURSIVE cte AS (
   (
   SELECT t  -- whole row
   FROM   tbl t
   WHERE  status IN ('ACCEPTED', 'CORRECTED')
   AND    NOT EXISTS (SELECT FROM tbl
                      WHERE  person_id = t.person_id 
                      AND   (  submission_date,   last_updated,   id)
                          > (t.submission_date, t.last_updated, t.id)  -- row-wise comparison
                      )
   ORDER  BY submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST
   LIMIT  1
   )

   UNION ALL
   SELECT (SELECT t1  -- whole row
           FROM   tbl t1
           WHERE ( t1.submission_date, t1.last_updated, t1.id)
               < ((t).submission_date,(t).last_updated,(t).id)  -- row-wise comparison
           AND    t1.status IN ('ACCEPTED', 'CORRECTED')
           AND    NOT EXISTS (SELECT FROM tbl
                              WHERE  person_id = t1.person_id 
                              AND   (   submission_date,    last_updated,    id)
                                  > (t1.submission_date, t1.last_updated, t1.id)  -- row-wise comparison
                              )
           ORDER  BY submission_date DESC NULLS LAST, last_updated DESC NULLS LAST, id DESC NULLS LAST
           LIMIT  1)
   FROM   cte c
   WHERE  (t).id IS NOT NULL
   )
SELECT (t).*
FROM   cte
LIMIT  10
OFFSET 0;

Mọi tập hợp các dấu ngoặc đơn ở đây là bắt buộc.

Mức độ phức tạp này sẽ truy xuất một tập hợp tương đối nhỏ các hàng trên cùng nhanh hơn hoàn toàn bằng cách sử dụng các chỉ số đã cho và không cần quét tuần tự. Xem:

submission_date hầu hết có lẽ nên là loại timestamptz hoặc date , không character varying(255) - đó là một định nghĩa kiểu kỳ quặc trong Postgres trong mọi trường hợp. Xem:

Nhiều chi tiết khác có thể được tối ưu hóa, nhưng điều này đang vượt quá tầm tay. Bạn có thể cân nhắc việc tư vấn chuyên nghiệp.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Làm cách nào tôi có thể nghe cơ sở dữ liệu postgresql với SignalR Core trong dự án lõi .net?

  2. Rails:quyền bị từ chối đối với quan hệ schema_migrations

  3. Loại trừ các định nghĩa hàm khi kết xuất cơ sở dữ liệu PostgreSQL

  4. Sự khác biệt giữa Postgres DISTINCT và DISTINCT ON là gì?

  5. Truyền hoặc trích xuất dấu thời gian từ v1 UUID trong PostgreSQL