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


170

IDの大きなリストを取得するクエリを記述する必要があります。

多くのバックエンド(MySQL、Firebird、SQLServer、Oracle、PostgreSQLなど)をサポートしているため、標準SQLを作成する必要があります。

IDセットのサイズは大きくなる可能性があり、クエリはプログラムで生成されます。それで、最善のアプローチは何ですか?

1)INを使用してクエリを作成する

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

ここで私の質問です。nが非常に大きい場合はどうなりますか?また、パフォーマンスについてはどうですか?

2)ORを使用してクエリを記述する

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

このアプローチにはn制限はないと思いますが、nが非常に大きい場合のパフォーマンスはどうでしょうか?

3)プログラムによるソリューションの作成:

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

データベースサーバーがネットワーク経由でクエリされたときに、このアプローチでいくつかの問題が発生しました。通常、小さなクエリをたくさん作成するよりも、すべての結果を取得する1つのクエリを実行する方が適切です。たぶん私は間違っています。

この問題の正しい解決策は何でしょうか?


1
オプション1を使用すると、SQLサーバーの応答時間が大幅に短縮され、一部が存在しない7k IDが選択されます。通常、クエリの所要時間は約1300ミリ秒でしたが、IN!私はあなたの解決策1 + 3として採掘しました。最後のクエリは、実行するためにSQLに送信された1つの長いクエリ文字列でした。
Piotr Kula 2015

回答:


108

オプション1が唯一の優れたソリューションです。

どうして?

  • オプション2も同じことを行いますが、列名を何度も繰り返します。さらに、SQLエンジンは、値が固定リストの値の1つであるかどうかを確認する必要があることをすぐには認識しません。ただし、優れたSQLエンジンは、と同様のパフォーマンスを実現するように最適化できますIN。ただし、読みやすさの問題はまだあります...

  • オプション3は、パフォーマンスに関しては単にひどいものです。ループごとにクエリを送信し、小さなクエリでデータベースを操作します。また、「値が指定されたリストにある値の1つである」の最適化を使用できないようにします。


2
私は同意しますが、リストは多くのRDMSで制限されているため、@ Ed Guinessのソリューションを使用する必要がありますが、ここでは一時テーブルがRDBMS間で異なることに注意してください。(事実上、複雑な問題では純粋な標準SQLだけを使用することはできません)
mmmmmm '27

28

別の方法として、別のテーブルを使用してID値を含める方法があります。次に、この他のテーブルをTABLEで内部結合して、返される行を制約できます。これには、動的SQL(最良の場合は問題あり)を必要とせず、無限に長いIN句を使用できないという大きな利点があります。

この他のテーブルを切り捨て、多数の行を挿入してから、おそらくインデックスを作成して結合のパフォーマンスを向上させます。また、これらの行の蓄積をデータの取得から切り離すことができ、おそらくパフォーマンスを調整するためのより多くのオプションを提供します。

更新:一時テーブルを使用することはできますが、そうする必要がある、またはすべきであると示唆するつもりはありませんでした。一時データに使用される永続テーブルは、ここで説明されている以上のメリットを持つ一般的なソリューションです。


1
しかし、必要なIDのリストをどのように渡しますか?(あなたが範囲やそのようなものを選択できないのを見て)。
raam86 2016年

1
@ raam86:IDのリストはselect、別のテーブルのステートメントを使用して取得された可能性があります。リストは、対象となる他のテーブルとして渡されますinner join
bdforbes 2018

19

エド・ギネスが示唆したことは本当にパフォーマンスブースターです、私はこのようなクエリを持っていました

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

私がしたこと :

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

次に、内部がメインテーブルとtempを結合しました:

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

そしてパフォーマンスは劇的に向上しました。


1
こんにちは、fnSplitterはMSSQLの関数ですか?それを見つけることができなかったので。
WiiMaxx

それは標準的なものではありません。それらは、この目的のためにその関数を記述したこと、または、たとえばすでにそれを提供するアプリケーションを持っていることを意味しなければなりません。
underscore_d

fnSplitterはRituによって作成された関数であり、インターネット/ Googleで同様に見つけることができます
Bashar Abu Shamaa

9

最初のオプションは間違いなく最良のオプションです。

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

ただし、IDのリストが非常に大きい(数百万と言う)ことを考えると、以下のようなチャンクサイズを検討する必要があります。

  • IDのリストを固定数のチャンク(たとえば100)に分割します
  • チャンクサイズは、サーバーのメモリサイズに基づいて決定する必要があります
  • IDが10000あるとすると、10000/100 = 100チャンクになります。
  • 一度に1つのチャンクを処理して、selectに対して100回のデータベース呼び出しを行う

チャンクに分割する必要があるのはなぜですか?

あなたのようなシナリオでは非常に一般的であるメモリオーバーフローの例外を取得することはありません。データベース呼び出しの数が最適化され、パフォーマンスが向上します。

それはいつも私にとって魅力のように働いてきました。私の仲間の開発者にもうまくいくことを願っています:)


4

5億レコードのAzure SQLテーブルでSELECT * FROM MyTable where id in()コマンドを実行すると、待機時間が7分を超えました。

代わりにこれを実行すると、すぐに結果が返されます。

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

結合を使用します。


3

ほとんどのデータベースシステムIN (val1, val2, …)ORは、一連のものが同じ計画に最適化されています。

3番目の方法は、値のリストを一時テーブルにインポートして結合することです。これは、値が多数ある場合、ほとんどのシステムでより効率的です。

この記事を読むとよいでしょう。


3

サンプル3は、明らかな理由もなくデータベースを無数にヒットしているため、これらすべての中で最悪のパフォーマンスになります。

一時テーブルにデータをロードしてから結合するのが最速です。その後、INはORのグループよりもわずかに速く機能します。


2

私はSqlServerを意味すると思いますが、Oracleでは、指定できるIN要素の数に厳しい制限があります:1000。


1
SQL Serverでさえ、40万のIN要素の後で動作を停止します。MSDNによると:IN句に非常に多数の値(数千)を含めると、リソースが消費され、エラー8623または8632が返されます。この問題を回避するには、アイテムをテーブルのINリストに保存します。
jahav 2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.