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


170

Kimliklerin büyük bir listesini almak için bir sorgu yazmak gerekiyor.

Birçok arka ucunu (MySQL, Firebird, SQLServer, Oracle, PostgreSQL ...) destekliyoruz, bu yüzden standart bir SQL yazmam gerekiyor.

Kimlik kümesinin boyutu büyük olabilir, sorgu programlı olarak oluşturulur. Peki, en iyi yaklaşım nedir?

1) IN kullanarak sorgu yazma

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

Benim sorum bu. N çok büyükse ne olur? Peki ya performans?

2) VEYA kullanarak sorgu yazma

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

Bu yaklaşımın n sınırı olmadığını düşünüyorum, ama n çok büyükse performans ne olacak?

3) Programlı bir çözüm yazmak:

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

Veritabanı sunucusu ağ üzerinden sorgulandığında bu yaklaşımla ilgili bazı sorunlar yaşadık. Normalde, çok sayıda küçük sorgu yapmaya karşı tüm sonuçları alan bir sorgu yapmak daha iyidir. Belki de ben hatalıyım.

Bu sorun için doğru çözüm ne olabilir?


1
Seçenek 1, bazıları bulunmayan 7k ID'leri seçerek SQL sunucu yanıt süresini önemli ölçüde azaltır. Normalde sorgu yaklaşık 1300ms sürdü, kullanarak 80ms'ye düşer IN! Ben çözüm 1 + 3 olarak benim yaptı. Sadece son sorgu yürütmek için SQL gönderilen bir, uzun sorgu dizesi oldu.
Piotr Kula

Yanıtlar:


108

Seçenek 1 tek iyi çözümdür.

Neden?

  • Seçenek 2 aynı şeyi yapar, ancak sütun adını birçok kez tekrarlarsınız; Ayrıca SQL motoru, değerin sabit bir listedeki değerlerden biri olup olmadığını kontrol etmek istediğinizi hemen bilmez. Ancak, iyi bir SQL motoru gibi eşit performansa sahip olacak şekilde optimize edebilir IN. Yine de okunabilirlik sorunu var ...

  • Seçenek 3, sadece performans açısından korkunç. Her döngüde bir sorgu gönderir ve veritabanını küçük sorgular ile çekiçler. Ayrıca, "değer verilen listedeki değerlerden biridir" için herhangi bir optimizasyon kullanmasını da önler


2
Kabul ediyorum, ancak listenin birçok RDMS'de sınırlı olduğunu ve bu nedenle @Ed Guiness'in çözümünü kullanmamız gerektiğini unutmayın, ancak burada geçici tablolar RDBMS arasında farklılık gösterir. (Etkili karmaşık sorunlar için sadece saf standart SQL kullanamazsınız)
mmmmmm

28

Alternatif bir yaklaşım da id değerleri içermek için başka bir tablo kullanmak olabilir. Bu diğer tablo daha sonra döndürülen satırları sınırlamak için TABLO'nuza iç birleştirilebilir. Bu, dinamik SQL'e (en iyi ihtimalle sorunlu) ihtiyacınız olmayacak ve sonsuz uzunluğunda bir IN yan tümcesine sahip olmayacağınız büyük bir avantaja sahip olacaktır.

Bu diğer tabloyu kısaltır, çok sayıda satır ekler ve ardından birleştirme performansına yardımcı olacak bir dizin oluşturabilirsiniz. Ayrıca, bu satırların birikimini verilerin alınmasından ayırmanıza izin verir, belki de performansı ayarlamak için daha fazla seçenek sunar.

Güncelleme : Her ne kadar geçici bir masa kullanabilseniz de, mecbur olduğunuzu ya da hatta mecbur olduğunuzu ima etmek istemedim. Geçici veriler için kullanılan kalıcı bir tablo, burada açıklananın ötesinde değerlere sahip ortak bir çözümdür.


1
Ancak ihtiyacınız olan kimliklerin listesini nasıl iletirsiniz? (Bir aralık veya bunun gibi bir şey seçemeyeceğinizi görmek).
raam86

1
@ raam86: kimlik listesi selectbaşka bir tablodaki bir ifade kullanılarak alınmış olabilir . Liste, inner joinkarşı karşıya kaldığınız diğer tablo olarak geçirilir .
bdforbes

19

Ed Guiness önerdiği gerçekten bir performans yükseltici, böyle bir sorgu vardı

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

ben ne yaptım :

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

Sonra iç ana tablo ile temp katıldı:

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

Ve performans büyük ölçüde arttı.


1
Merhaba, fnSplitter MSSQL bir işlevi var mı? Çünkü bulamadım.
WiiMaxx

Standart bir şey değil. Bu işlevi bu amaçla yazdıkları veya örneğin zaten sağlayan bir uygulaması olduğu anlamına gelmelidir.
underscore_d

fnSplitter, Ritu tarafından oluşturulan bir işlevdir, internette / google'da buna benzer bulabilirsiniz
Bashar Abu Shamaa

9

İlk seçenek kesinlikle en iyi seçenektir.

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

Bununla birlikte , milyonlarca kişinin kimlikleri listesinin çok büyük olduğu göz önüne alındığında, aşağıdaki gibi yığın boyutlarını düşünmelisiniz:

  • Kimlikler listenizi sabit sayıdaki parçalara ayırın, örneğin 100
  • Yığın boyutu, sunucunuzun bellek boyutuna göre belirlenmelidir
  • 10000 Kd'niz olduğunu varsayalım, 10000/100 = 100 parça olacak
  • Seçim için 100 veritabanı çağrısı ile sonuçlanan bir kerede bir yığın işleyin

Neden parçalara bölünmelisin?

Sizinki gibi senaryolarda çok yaygın olan bellek taşması istisnasını asla alamazsınız. Daha iyi performans sağlayacak sayıda veritabanı çağrısını optimize etmiş olacaksınız.

Benim için her zaman cazibe gibi çalıştı. Umarım diğer geliştiricilerim için de çalışır :)


4

500 milyon kayıt içeren bir Azure SQL tablosunda id in () komutunun MyTable'dan SELECT * YAPILMASI,> 7 dakika bekleme süresiyle sonuçlandı!

Bunu yapmak sonuçları hemen döndürdü:

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

Birleştirme kullanın.


3

Çoğu veritabanı sisteminde IN (val1, val2, …)ve bir dizi ORaynı plan için optimize edilmiştir.

Üçüncü yol, değerler listesini geçici bir tabloya aktarmak ve çok sayıda değer varsa çoğu sistemde daha verimli olan tabloya katılmak olacaktır.

Bu makaleleri okumak isteyebilirsiniz:


3

Örnek 3, bunların hiçbirini en kötü performans gösteren kişi olacaktır, çünkü veritabanını sayısız kez belirgin bir nedenden ötürü vuruyorsunuz.

Verileri geçici tabloya yüklemek ve sonra buna katılmak en hızlısıdır. Bundan sonra IN, OR grubundan biraz daha hızlı çalışmalıdır.


2

Sanırım SqlServer kastediyorsunuz ama Oracle'da kaç tane IN elemanı belirtebileceğiniz konusunda zor bir sınırınız var: 1000.


1
SQL Server bile ~ 40k IN öğelerinden sonra çalışmayı durdurur. MSDN'ye göre: Bir IN yan tümcesine çok fazla sayıda değer (binlerce) dahil etmek kaynakları tüketebilir ve 8623 veya 8632 hatalarını döndürebilir. Bu soruna geçici bir çözüm bulmak için <a0> </a0>, IN listesinde öğeleri bir tabloda depolayın.
jahav
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.