Bisakah PostgreSQL menggunakan nulls dalam indeksnya?

10

Saya telah membaca buku ini yang mengatakan itu

Basis data mengasumsikan bahwa Indexed_Col BUKAN NULL mencakup rentang yang terlalu besar untuk berguna, sehingga basis data tidak akan mengarahkan ke indeks dari kondisi ini.

Saya menyadari bahwa buku itu sudah lebih dari 10 tahun, tetapi sudah terbukti cukup berguna - Dengan menggunakan instruksi yang diperoleh dari halaman-halamannya, saya telah mempercepat kueri dengan faktor sepuluh.

Selanjutnya, dalam menjalankan EXPLAIN ANALYZEpada SELECTquery, saya telah menemukan bahwa tidak ada indeks saya sedang digunakan, bahkan ketika semua hak, mereka seharusnya.

Jadi, pertanyaan saya adalah:

Andaikata ada tabel yang memiliki kolom, yang definisi kolomnya mencakup "BUKAN NULL", dan bahwa ada indeks yang mencakup kolom ini, akankah indeks ini digunakan dalam kueri dari tabel itu di mana kolom merupakan bagian dari kueri?

Suka:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;
FuriousFolder
sumber

Jawaban:

9

PostgreSQL tentu saja dapat menggunakan indeks untuk IS NOT NULL. Saya juga tidak melihat asumsi perencana kueri tentang kondisi itu.

Jika fraksi nol untuk kolom ( pg_statistic.stanullfrac) cukup rendah untuk menunjukkan bahwa indeks selektif berguna untuk kueri, PostgreSQL akan menggunakan indeks.

Saya tidak tahu apa yang ingin Anda katakan:

Jika ini benar, apakah pemahaman saya bahwa indeks pada kolom didefinisikan sebagai "BUKAN NULL" tidak digunakan dalam kueri yang menggunakan kolom itu?

Tentu saja indeks tidak akan digunakan untuk suatu IS NOT NULLkondisi pada NOT NULLkolom. Itu akan selalu cocok dengan 100% dari baris, sehingga seqscan akan hampir selalu jauh lebih cepat.

PostgreSQL tidak akan menggunakan indeks jika indeks tidak menyaring sebagian besar baris untuk permintaan. Satu-satunya pengecualian adalah ketika Anda meminta satu set kolom yang dicakup oleh satu indeks, dalam urutan yang cocok dengan indeks. PostgreSQL mungkin akan melakukan scan indeks saja. Misalnya jika ada indeks t(a, b, c)dan Anda:

select a, b FROM t ORDER BY a, b, c;

PostgreSQL mungkin menggunakan indeks Anda, meskipun tidak ada baris yang difilter, karena hanya perlu membaca indeks dan dapat melewati membaca heap, hindari melakukan pengurutan, dll.

Craig Ringer
sumber
Ini semua berlaku pada PG 9.0
eradman
1
Dan bahkan pada kolom yang dapat dibatalkan, kueri dengan kondisi WHERE column IS NOT NULLtidak dapat menggunakan indeks karena, seperti yang dikatakan buku: "mencakup rentang yang terlalu besar untuk berguna,". Jika 90% dari nilai-nilai tersebut tidak nol, seqscan mungkin akan lebih cepat juga.
ypercubeᵀᴹ
Persis. Mungkin, tetapi hanya jika sebagian besar dari tabel adalah nol. Seringkali dalam hal ini indeks parsial adalah pilihan yang lebih baik.
Craig Ringer
Iya. Saya mencoba untuk mengatakan bahwa (seperti yang saya mengerti) bagian "mencakup rentang yang terlalu besar" mengacu pada indeks tetapi dalam hal kondisi spesifik dan bukan indeks secara umum.
ypercubeᵀᴹ
2
@FuriousFolder Heh, ada terlalu banyak negasi di sini. PostgreSQL tidak akan menggunakan indeks pada NOT NULLkolom untuk IS NOT NULLkueri kecuali indeks itu juga berguna untuk bagian lain dari WHEREklausa, bergabung dengan filter, dll, atau dapat digunakan untuk pemindaian hanya indeks yang dipesan. Dengan kata lain, itu akan sepenuhnya mengabaikan redundan IS NOT NULLpada NOT NULLkolom dan membuat pilihan penggunaan indeks berdasarkan detail lainnya. (Lihat edit, pindai ulang hanya indeks).
Craig Ringer
2

Selain jawaban menyeluruh Craig, saya ingin menambahkan bahwa sampul buku yang Anda referensi mengatakan:

Meliputi Oracle, DB2 & SQL Server

Jadi saya tidak akan mempercayainya untuk menjadi sumber saran yang bagus tentang PostgreSQL pada khususnya. Setiap RDBMS bisa sangat berbeda!

Saya agak bingung tentang pertanyaan awal Anda, tetapi inilah contoh yang menunjukkan bahwa bagian buku ini tidak 100% benar. Untuk menghindari kebingungan lebih lanjut, inilah keseluruhan paragraf yang relevan, Anda dapat melihatnya di Pencarian Buku Google .

Basis data mengasumsikan bahwa Indexed_Col BUKAN NULL mencakup rentang yang terlalu besar untuk berguna, sehingga basis data tidak akan mengarahkan ke indeks dari kondisi ini. Dalam kasus yang jarang terjadi, memiliki nilai nonnull sangat jarang sehingga pemindaian rentang indeks atas semua nilai nonnull yang mungkin bermanfaat. Dalam kasus seperti itu, jika Anda dapat mengetahui batas bawah atau atas yang aman untuk kisaran semua nilai yang mungkin, Anda dapat mengaktifkan pemindaian rentang dengan kondisi seperti Positive_ID_Column> -1 atau Date_Column> TO_DATE ('0001/01/01' , 'YYYY / MM / DD').

Postgres sebenarnya dapat (dalam kasus yang dibuat berikut) menggunakan indeks untuk memenuhi IS NOT NULLkueri tanpa menambahkan kludges pindai rentang seperti yang disarankan Positive_ID_Column > -1. Lihat komentar pada pertanyaan Craig mengapa Postgres memilih indeks ini dalam kasus khusus ini, dan catatan tentang menggunakan indeks parsial.

CREATE TABLE bar (a int);
INSERT INTO bar (a) SELECT NULL FROM generate_series(1,1000000);
INSERT INTO bar (a) VALUES (1);
CREATE INDEX bar_idx ON bar (a);

EXPLAIN ANALYZE SELECT * FROM bar WHERE a IS NOT NULL;
                                                QUERY PLAN                                                    
------------------------------------------------------------------------------------------------------------------
 Index Only Scan using bar_idx on bar  (cost=0.42..8.44 rows=1 width=4) (actual time=0.094..0.095 rows=1 loops=1)
   Index Cond: (a IS NOT NULL)
   Heap Fetches: 1
 Total runtime: 0.126 ms
(4 rows)

Ngomong-ngomong, ini adalah Postgres 9.3, tapi saya yakin hasilnya akan hampir sama pada 9.1, meskipun tidak akan menggunakan "Indeks Hanya Pindai".

Sunting: Saya melihat Anda telah mengklarifikasi pertanyaan awal Anda, dan Anda tampaknya bertanya-tanya mengapa Postgres tidak menggunakan indeks dalam contoh sederhana seperti:

CREATE TABLE my_table(
a varchar NOT NULL
);

CREATE INDEX ix_my_table ON my_table(a);

SELECT a from my_table;

Mungkin karena Anda tidak memiliki baris di tabel. Jadi tambahkan beberapa data uji dan ANALYZE my_table;.

Josh Kupershmidt
sumber
Dalam uraian buku tersebut (penekanan tambang): "Penulis Dan Tow menguraikan metode penghematan waktu yang telah ia kembangkan untuk menemukan rencana eksekusi optimal - dengan cepat dan sistematis - terlepas dari kompleksitas SQL atau platform basis data yang digunakan " Juga, mungkin Anda mengabaikan # 1 dari pertanyaan, yaitu, bahwa kolom didefinisikan sebagai NOT NULL, bukan permintaan yang digunakan IS NOT NULLsebagai kondisi indeksnya. Ini ada dalam komentar yang Anda referensikan, tetapi saya akan memperbarui pertanyaan untuk memasukkannya.
FuriousFolder
Selanjutnya, buku itu sendiri adalah agnostik bahasa: satu-satunya bagian khusus
DMBS
1
@FuriousFolder kolom didefinisikan sebagai BUKAN NULL tetapi bagian ini (dalam pertanyaan Anda, dari buku): "bahwa Indexed_Col BUKAN NULL meliputi ..." mengacu pada kondisi di mana dan bukan definisi kolom. Walaupun sulit untuk dipastikan, karena itu di luar konteks. Mungkin Anda harus memasukkan seluruh paragraf (sebelumnya) dari buku ini.
ypercubeᵀᴹ
-1

Anda belum mengirimkan kueri atau data contoh Anda. Tetapi alasan paling umum indeks tidak digunakan berkaitan dengan volume.

Indeks seperti buku telepon yang menerjemahkan kolom ke lokasi baris. Jika Anda hanya mencari beberapa baris, masuk akal untuk mencari setiap baris dalam buku telepon, dan kemudian mencari baris di tabel utama.

Tetapi untuk lebih dari beberapa baris, lebih murah untuk melewatkan buku telepon, dan beralih ke semua baris di tabel utama. Dalam pengalaman saya, titik kritis sekitar 100 baris.

Andomar
sumber
"Indeks seperti buku telepon yang menerjemahkan kolom ke lokasi baris. Jika Anda hanya mencari beberapa baris, masuk akal untuk mencari setiap baris dalam buku telepon, dan kemudian mencari baris di tabel utama." Sebenarnya, indeks seperti buku telepon yang lebih kecil yang diperbarui kapan saja buku telepon yang mereka indeks diperbarui. Anda tahu bahwa setiap kali Anda membuka buku telepon yang lebih kecil, Anda akan menemukan semua dan semua informasi yang dijelaskan oleh kondisi pengindeksannya. Misalnya Semua orang yang bernama 'terang' di meja indeks: CREATE INDEX ix_frank ON people(name) WHERE name ='frank'.
FuriousFolder
Ini memungkinkan pemindaian hanya indeks jauh lebih cepat, karena Anda dapat membaca seluruh "buku telepon yang lebih kecil" ke dalam memori, yang tidak layak dengan tabel berjuta-juta baris.
FuriousFolder
@FuriousFolder: Anda menggambarkan pemindaian hanya indeks. Tetapi OP mengatakan bahwa indeksnya tidak digunakan, yang tidak akan terjadi jika pemindaian hanya indeks akan memenuhi permintaan.
Andomar
Andomar ... Akulah OP, haha. Tujuan saya persis seperti itu; untuk mendapatkan kueri ini menggunakan pemindaian hanya indeks. Saya sejak mencapai hal itu, karena Craig menjelaskan bahwa postgres adalah dapat menggunakan indeks pada kolom di mana definisi kolom meliputi NOT NULL
FuriousFolder