SQL WHERE ID IN (id1, id2, ..., idn)

170

Saya perlu menulis kueri untuk mengambil daftar besar id.

Kami mendukung banyak backend (MySQL, Firebird, SQLServer, Oracle, PostgreSQL ...) jadi saya perlu menulis SQL standar.

Ukuran set id bisa besar, kueri akan dihasilkan secara terprogram. Jadi, apa pendekatan terbaik?

1) Menulis kueri menggunakan IN

SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)

Pertanyaan saya di sini adalah. Apa yang terjadi jika n sangat besar? Juga, bagaimana dengan kinerja?

2) Menulis kueri menggunakan OR

SELECT * FROM TABLE WHERE ID = id1 OR ID = id2 OR ... OR ID = idn

Saya pikir pendekatan ini tidak memiliki batas n, tetapi bagaimana dengan kinerja jika n sangat besar?

3) Menulis solusi terprogram:

  foreach (var id in myIdList)
  {
      var item = GetItemByQuery("SELECT * FROM TABLE WHERE ID = " + id);
      myObjectList.Add(item);
  }

Kami mengalami beberapa masalah dengan pendekatan ini ketika server database ditanyai melalui jaringan. Biasanya lebih baik melakukan satu kueri yang mengambil semua hasil dibandingkan membuat banyak kueri kecil. Mungkin aku salah.

Apa solusi yang tepat untuk masalah ini?

Daniel Peñalba
sumber
1
Opsi 1 secara signifikan mengurangi waktu respons server SQL, memilih ID 7k, yang beberapa di antaranya tidak ada. Biasanya kueri membutuhkan waktu sekitar 1300 ms, dikurangi menjadi 80 ms menggunakan IN! Saya melakukan tambang sebagai solusi Anda 1 + 3. Hanya kueri terakhir adalah satu, string kueri panjang dikirim ke SQL untuk dieksekusi.
Piotr Kula

Jawaban:

108

Opsi 1 adalah satu-satunya solusi yang baik.

Mengapa?

  • Opsi 2 melakukan hal yang sama tetapi Anda mengulangi nama kolom berkali-kali; Selain itu mesin SQL tidak segera tahu bahwa Anda ingin memeriksa apakah nilainya adalah salah satu nilai dalam daftar tetap. Namun, mesin SQL yang baik dapat mengoptimalkannya untuk memiliki kinerja yang sama dengan IN. Masih ada masalah keterbacaan ...

  • Opsi 3 adalah kinerja-bijaksana hanya mengerikan. Ini mengirimkan kueri setiap loop dan memalu database dengan permintaan kecil. Itu juga mencegahnya menggunakan optimasi apa pun untuk "nilai adalah salah satu dari yang ada dalam daftar"

Pencuri
sumber
2
Saya setuju tetapi perhatikan bahwa daftar dalam terbatas di banyak RDMS dan jadi Anda perlu kami menggunakan solusi @Ed Guiness tetapi di sini tabel sementara berbeda antara RDBMS. (Secara efektif untuk masalah kompleks Anda tidak dapat menggunakan SQL standar murni saja)
mmmmmm
28

Pendekatan alternatif mungkin menggunakan tabel lain untuk memuat nilai id. Tabel lain ini kemudian dapat digabungkan di dalam TABEL Anda untuk membatasi baris yang dikembalikan. Ini akan memiliki keuntungan besar bahwa Anda tidak akan memerlukan SQL dinamis (bermasalah pada saat terbaik), dan Anda tidak akan memiliki klausa IN yang sangat panjang.

Anda akan memotong tabel lain ini, memasukkan banyak baris Anda, lalu mungkin membuat indeks untuk membantu kinerja bergabung. Ini juga akan memungkinkan Anda melepaskan akumulasi baris-baris ini dari pengambilan data, mungkin memberi Anda lebih banyak opsi untuk menyempurnakan kinerja.

Pembaruan : Meskipun Anda bisa menggunakan tabel sementara, saya tidak bermaksud mengatakan bahwa Anda harus atau bahkan harus. Tabel permanen yang digunakan untuk data sementara adalah solusi umum dengan kelebihan di luar yang dijelaskan di sini.

Ed Guinness
sumber
1
Tetapi bagaimana Anda akan melewati daftar id yang Anda butuhkan? (Melihat Anda tidak dapat memilih rentang atau sesuatu seperti itu).
raam86
1
@ raam86: daftar ID mungkin diperoleh dengan menggunakan selectpernyataan di tabel lain. Daftar ini diteruskan sebagai tabel lain yang Anda inner joinlawan.
bdforbes
19

Yang disarankan Ed Guiness benar-benar penguat kinerja, saya punya pertanyaan seperti ini

select * from table where id in (id1,id2.........long list)

apa yang saya lakukan :

DECLARE @temp table(
            ID  int
            )
insert into @temp 
select * from dbo.fnSplitter('#idlist#')

Kemudian bagian dalam bergabung dengan temp dengan tabel utama:

select * from table inner join temp on temp.id = table.id

Dan kinerjanya meningkat secara drastis.

Ritu
sumber
1
Hai, apakah fnSplitter adalah fungsi dari MSSQL? Karena saya tidak dapat menemukannya.
WiiMaxx
Itu bukan hal standar. Mereka harus berarti bahwa mereka menulis fungsi itu untuk tujuan ini, atau misalnya memiliki aplikasi yang sudah menyediakannya.
underscore_d
fnSplitter adalah fungsi yang dibuat oleh Ritu, Anda dapat menemukannya di internet / mirip dengan Google
Bashar Abu Shamaa
9

Opsi pertama jelas merupakan pilihan terbaik.

SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)

Namun mengingat daftar id sangat besar , katakan jutaan, Anda harus mempertimbangkan ukuran chunk seperti di bawah ini:

  • Bagilah Anda daftar Id menjadi potongan nomor tetap, katakan 100
  • Ukuran chunk harus ditentukan berdasarkan ukuran memori server Anda
  • Misalkan Anda memiliki 10.000 Id, Anda akan memiliki 10000/100 = 100 chunks
  • Memproses satu potong sekaligus untuk menghasilkan 100 panggilan basis data untuk dipilih

Mengapa Anda harus membaginya menjadi potongan-potongan?

Anda tidak akan pernah mendapatkan pengecualian limpahan memori yang sangat umum dalam skenario seperti milik Anda. Anda akan memiliki jumlah panggilan basis data yang dioptimalkan sehingga menghasilkan kinerja yang lebih baik.

Itu selalu bekerja seperti pesona bagi saya. Semoga ini akan bekerja untuk sesama pengembang saya juga :)

Adarsh ​​Kumar
sumber
4

Melakukan SELECT * FROM MyTable where perintah id in () pada tabel Azure SQL dengan 500 juta catatan menghasilkan waktu tunggu> 7 menit!

Melakukan ini sebagai gantinya mengembalikan hasil:

select b.id, a.* from MyTable a
join (values (250000), (2500001), (2600000)) as b(id)
ON a.id = b.id

Gunakan gabung.

JakeJ
sumber
3

Dalam kebanyakan sistem basis data, IN (val1, val2, …)dan serangkaian ORdioptimalkan untuk rencana yang sama.

Cara ketiga adalah mengimpor daftar nilai ke tabel sementara dan menggabungkannya yang lebih efisien di sebagian besar sistem, jika ada banyak nilai.

Anda mungkin ingin membaca artikel ini:

Quassnoi
sumber
3

Contoh 3 akan menjadi yang terburuk di antara mereka semua karena Anda menekan database berkali-kali tanpa alasan yang jelas.

Memuat data ke tabel temp dan kemudian bergabung dengan itu akan menjadi yang tercepat. Setelah itu IN harus bekerja sedikit lebih cepat daripada kelompok OR.

judda
sumber
2

Saya pikir maksud Anda SqlServer tetapi pada Oracle Anda memiliki batas keras berapa banyak elemen IN Anda dapat menentukan: 1000.

flq
sumber
1
Bahkan SQL Server berhenti bekerja setelah elemen ~ 40k IN. Menurut MSDN: Termasuk sejumlah besar nilai (ribuan) dalam klausa IN dapat mengkonsumsi sumber daya dan mengembalikan kesalahan 8623 atau 8632. Untuk mengatasi masalah ini, simpan item dalam daftar IN dalam tabel.
jahav