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

SQL:Ketika berbicara tentang NOT IN dan NOT EQUAL TO, mana yang lebih efisien dan mengapa?

Di PostgreSQL biasanya ada perbedaan yang cukup kecil pada panjang daftar yang masuk akal, meskipun IN jauh lebih bersih secara konseptual. Sangat panjang AND ... <> ... daftar dan sangat panjang NOT IN daftar keduanya berkinerja buruk, dengan AND jauh lebih buruk daripada NOT IN .

Dalam kedua kasus tersebut, jika cukup lama bagi Anda untuk mengajukan pertanyaan, Anda harus melakukan tes pengecualian anti-gabung atau subkueri melalui daftar nilai.

WITH excluded(item) AS (
    VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT * 
FROM thetable t
WHERE NOT EXISTS(SELECT 1 FROM excluded e WHERE t.item = e.item);

atau:

WITH excluded(item) AS (
    VALUES('item1'), ('item2'), ('item3'), ('item4'),('item5')
)
SELECT * 
FROM thetable t
LEFT OUTER JOIN excluded e ON (t.item = e.item)
WHERE e.item IS NULL;

(Pada versi Pg modern, keduanya akan menghasilkan paket kueri yang sama).

Jika daftar nilai cukup panjang (puluhan ribu item) maka penguraian kueri mungkin mulai memiliki biaya yang signifikan. Pada titik ini Anda harus mempertimbangkan untuk membuat TEMPORARY tabel, COPY ing data untuk dikecualikan ke dalamnya, mungkin membuat indeks di atasnya, kemudian menggunakan salah satu pendekatan di atas pada tabel temp alih-alih CTE.

Demo:

CREATE UNLOGGED TABLE exclude_test(id integer primary key);
INSERT INTO exclude_test(id) SELECT generate_series(1,50000);
CREATE TABLE exclude AS SELECT x AS item FROM generate_series(1,40000,4) x;

di mana exclude adalah daftar nilai yang harus dihilangkan.

Saya kemudian membandingkan pendekatan berikut pada data yang sama dengan semua hasil dalam milidetik:

  • NOT IN daftar:3424.596
  • AND ... daftar:80173.823
  • VALUES berdasarkan JOIN pengecualian:20.727
  • VALUES pengecualian subkueri berbasis:20.495
  • Berbasis tabel JOIN , tidak ada indeks pada daftar sebelumnya:25.183
  • Berdasarkan tabel subkueri, tidak ada indeks pada daftar sebelumnya:23.985

... membuat pendekatan berbasis CTE lebih dari tiga ribu kali lebih cepat daripada AND daftar dan 130 kali lebih cepat daripada NOT IN daftar.

Kodenya di sini:https://gist.github.com/ringerc/5755247 (tutupi matamu, kamu yang mengikuti tautan ini).

Untuk ukuran kumpulan data ini, menambahkan indeks pada daftar pengecualian tidak ada bedanya.

Catatan:

  • IN daftar yang dihasilkan dengan SELECT 'IN (' || string_agg(item::text, ',' ORDER BY item) || ')' from exclude;
  • AND daftar dibuat dengan SELECT string_agg(item::text, ' AND item <> ') from exclude; )
  • Pengecualian tabel berbasis subkueri dan gabungan hampir sama di seluruh proses berulang.
  • Pemeriksaan rencana menunjukkan bahwa Pg menerjemahkan NOT IN ke <> ALL

Jadi... Anda dapat melihat bahwa ada yang benar-benar besar kesenjangan antara keduanya IN dan AND daftar vs melakukan gabungan yang benar. Yang mengejutkan saya adalah seberapa cepat melakukannya dengan CTE menggunakan VALUES list adalah ... menguraikan VALUES list hampir tidak memakan waktu sama sekali, melakukan hal yang sama atau sedikit lebih cepat dari pendekatan tabel di sebagian besar pengujian.

Alangkah baiknya jika PostgreSQL dapat secara otomatis mengenali IN . yang sangat panjang klausa atau rantai dari AND . yang serupa kondisi dan beralih ke pendekatan yang lebih cerdas seperti melakukan gabungan hash atau secara implisit mengubahnya menjadi simpul CTE. Saat ini ia tidak tahu bagaimana melakukannya.

Lihat juga:

  • postingan blog praktis yang ditulis Magnus Hagander tentang topik ini


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. haruskah saya mengaktifkan kumpulan pernyataan c3p0?

  2. Persimpangan beberapa array di PostgreSQL

  3. Memanfaatkan pg_prewarm dan pg_hibernator caching contrib di PostgreSQL 9.4.

  4. Fungsi PostgreSQL / Prosedur Tersimpan CURRENT_TIMESTAMP tidak berubah

  5. Postgres ERROR:tidak dapat membuka file untuk dibaca:Izin ditolak