Pemindaian Seq Tidak Terduga saat melakukan kueri terhadap boolean dengan nilai NULL

10

Saya memiliki kolom basis data yang disebut auto_reviewtipe kolom boolean. Ada indeks untuk bidang itu, dibuat menggunakan ORM ActiveRecord.

CREATE INDEX index_table_on_auto_renew ON table USING btree (auto_renew);

Saat saya meminta bidang untuk nilai boolean, PG menggunakan indeks seperti yang diharapkan.

EXPLAIN for: SELECT "table".* FROM "table"  WHERE "table"."auto_renew" = 'f'
                                          QUERY PLAN
----------------------------------------------------------------------------------------------
 Bitmap Heap Scan on table  (cost=51.65..826.50 rows=28039 width=186)
   Filter: (NOT auto_renew)
   ->  Bitmap Index Scan on index_domains_on_auto_renew  (cost=0.00..44.64 rows=2185 width=0)
         Index Cond: (auto_renew = false)
(4 rows)

Saat nilainya NULL, pemindaian berurutan digunakan.

EXPLAIN for: SELECT "table".* FROM "table"  WHERE "table"."auto_renew" IS NULL
                           QUERY PLAN
----------------------------------------------------------------
 Seq Scan on table  (cost=0.00..1094.01 rows=25854 width=186)
   Filter: (auto_renew IS NULL)
(2 rows)

Saya ingin tahu alasan di balik pilihan ini.

Simone Carletti
sumber

Jawaban:

19

Secara umum, col IS NULLadalah kandidat yang memungkinkan untuk pencarian indeks b-tree (default). Manual :

Juga, IS NULLatau IS NOT NULLkondisi pada kolom indeks dapat digunakan dengan indeks B-tree.

Untuk mendapatkan bukti, nonaktifkan pemindaian berurutan (hanya dalam sesi tes!):

SET enable_seqscan = OFF;

Saya mengutip manualnya di sini :

enable_seqscan (boolean)

Mengaktifkan atau menonaktifkan penggunaan jenis rencana pemindaian sekuensial oleh perencana permintaan. Tidak mungkin untuk menekan pindaian berurutan sepenuhnya, tetapi mematikan variabel ini membuat perencana tidak menggunakannya jika ada metode lain yang tersedia. Standarnya aktif.

Kemudian coba lagi:

EXPLAIN ANALYZE SELECT * FROM tbl WHERE auto_renew IS NULL;

Ini mungkin akan menghasilkan pemindaian indeks bitmap yang lebih lambat daripada pemindaian berurutan di atas meja.

Atur ulang, atau tutup sesi (pengaturannya adalah sesi-lokal).

RESET enable_seqscan;

Indeks pada booleankolom hanya berguna dalam kasus-kasus tertentu. Perencana hanya menggunakan indeks jika mengharapkan itu lebih cepat. Perhitungan didasarkan pada pengaturan biaya Anda dan statistik yang dikumpulkan oleh ANALYZE. Jika sebagian besar tabel cocok dengan kondisi Anda (sekitar 5% atau lebih, tergantung), biasanya lebih cepat melakukan pemindaian tabel penuh.

Ini meninggalkan nilai langka dalam booleankolom sebagai satu-satunya kandidat yang berguna untuk indeks biasa. Dan biasanya lebih efisien untuk membuat indeks parsial (lebih khusus) daripada ini - yang lebih murah untuk mempertahankan, lebih kecil, lebih cepat dan lebih mudah digunakan jika kondisi kueri cocok.

Jika Anda memiliki banyak kueri yang mencari baris auto_renew IS NULLdan NULLkasingnya tidak terlalu umum (dan / atau Anda memerlukan urutan pengurutan tertentu) maka indeks ini akan membantu menemukan / mengurutkan baris-baris ini dengan cepat:

CREATE INDEX index_tbl_tbl_id_auto_renew_null ON tbl (tbl_id)
WHERE auto_renew IS NULL;

Kondisi indeks parsial harus diulangi dalam WHEREklausa kueri kurang lebih tepat untuk membuat perencana kueri menyadari indeks berlaku.

Kolom yang diindeks ( tbl_id) adalah pilihan acak. Bagian yang penting adalah WHEREklausa. Indeks khusus ini akan paling efektif untuk kueri dengan ORDER BY tbl_idatau filter tambahan atau bergabung tbl_id. Anda bisa menjadikannya indeks multikolom . Kolom Boolean seringkali lebih berguna dalam kombinasi dengan yang lain.

Selain: ORM adalah kruk yang secara teratur gagal mendapatkan potensi penuh dari RDBMS Anda.

Erwin Brandstetter
sumber
Jawaban yang brilian, terima kasih Erwin. Saya sedih saya tidak bisa memperbaikinya dua kali.
Simone Carletti