PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Dapatkan baris yang diberi halaman dan jumlah total dalam satu kueri

Hal pertama yang pertama:Anda bisa gunakan hasil dari CTE beberapa kali dalam kueri yang sama, itulah fitur utama dari CTE .) Apa yang Anda miliki akan bekerja seperti ini (sementara masih menggunakan CTE sekali saja):

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

Peringatan 1:rank()

rank() dapat mengembalikan beberapa baris per person_id dengan rank = 1 . DISTINCT ON (person_id) (seperti yang disediakan Gordon) adalah pengganti yang berlaku untuk row_number() - yang bekerja untuk Anda, sebagai info tambahan diklarifikasi. Lihat:

Peringatan 2:ORDER BY submission_date DESC

Baik submission_date atau last_updated didefinisikan NOT NULL . Dapat menjadi masalah dengan ORDER BY submission_date DESC, last_updated DESC ... Lihat:

Haruskah kolom itu benar-benar NOT NULL ?

Anda menjawab:

String kosong tidak diperbolehkan untuk tipe date . Jaga agar kolom tetap nullable. NULL adalah nilai yang tepat untuk kasus tersebut. Gunakan NULLS LAST seperti yang ditunjukkan untuk menghindari NULL sedang diurutkan di atas.

Peringatan 3:OFFSET

Jika OFFSET sama atau lebih besar dari jumlah baris yang dikembalikan oleh CTE, Anda mendapatkan tidak ada baris , jadi juga tidak ada jumlah total. Lihat:

Solusi sementara

Mengatasi semua peringatan sejauh ini, dan berdasarkan informasi tambahan, kami mungkin sampai pada pertanyaan ini:

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;

Sekarang CTE sebenarnya digunakan dua kali. RIGHT JOIN menjamin kami mendapatkan jumlah total, tidak peduli OFFSET . DISTINCT ON harus melakukan OK-ish untuk hanya beberapa baris per (person_id) dalam kueri dasar.

Tapi Anda memiliki baris yang lebar. Berapa lebar rata-rata? Kueri kemungkinan akan menghasilkan pemindaian berurutan di seluruh tabel. Indeks tidak akan membantu (banyak). Semua ini akan tetap sangat tidak efisien untuk paging . Lihat:

Anda tidak dapat melibatkan indeks untuk paging karena indeks tersebut didasarkan pada tabel turunan dari CTE. Dan kriteria pengurutan Anda yang sebenarnya untuk paging masih belum jelas (ORDER BY id ?). Jika paging adalah tujuannya, Anda sangat membutuhkan gaya kueri yang berbeda. Jika Anda hanya tertarik pada beberapa halaman pertama, Anda memerlukan gaya kueri yang berbeda. Solusi terbaik tergantung pada informasi yang masih hilang dalam pertanyaan ...

Lebih cepat secara radikal

Untuk tujuan Anda yang diperbarui:

(Mengabaikan "untuk kriteria filter tertentu, jenis, rencana, status" untuk kesederhanaan.)

Dan:

Berdasarkan dua indeks khusus ini :

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);

Jalankan kueri ini:

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;

Setiap set tanda kurung di sini wajib diisi.

Tingkat kecanggihan ini harus mengambil satu set baris teratas yang relatif kecil secara radikal lebih cepat dengan menggunakan indeks yang diberikan dan tanpa pemindaian berurutan. Lihat:

submission_date kemungkinan besar harus ketik timestamptz atau date , bukan character varying(255) - yang merupakan definisi tipe aneh di Postgres dalam hal apa pun. Lihat:

Lebih banyak detail mungkin dioptimalkan, tetapi ini tidak terkendali. Anda dapat mempertimbangkan konsultasi profesional.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Gunakan pemicu pada tabel yang diwarisi untuk mengganti kunci asing

  2. PostgreSQL - Tetapkan nilai untuk setiap baris berdasarkan kriteria

  3. PILIH ADA vs. BATAS 1

  4. Dapatkan Nama Hari dari Tanggal di PostgreSQL

  5. Sequelize.js:Permintaan untuk tidak dalam array ($ne untuk item dalam array)