web-dev-qa-db-ja.com

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

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 (id in myIdList)
  {
      item = GetItemByQuery("SELECT * FROM TABLE WHERE ID = " + id);
      myObjectList.Add(item);
  }

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

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

134
Daniel Peñalba

オプション1が唯一の適切なソリューションです。

どうして?

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

  • オプション3は、パフォーマンス面で恐ろしいものです。ループごとにクエリを送信し、小さなクエリを使用してデータベースをハンマー処理します。また、「値は指定されたリスト内の値の1つ」に対する最適化を使用できなくなります。

86
ThiefMaster

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

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

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

23
Ed Guiness

Ed Guinessが提案したのは、実際にはパフォーマンスブースターです。次のようなクエリがありました。

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

また、パフォーマンスが大幅に向上しました。

10
Ritu

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

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

ただし、IDのリストは非常に大きいと考えられます、たとえば数百万、以下のようなチャンクサイズを考慮する必要があります。

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

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

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

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

7
Adarsh Kumar

サンプル3は、明確な理由もなく何度もデータベースにアクセスしているため、これらの中で最もパフォーマンスの低いものになります。

データを一時テーブルにロードしてから、そのテーブルに参加するのは、はるかに高速です。その後、INはORのグループよりもわずかに速く動作するはずです。

3
judda

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

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

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

結合を使用します。

3
JakeJ

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

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

次の記事をご覧ください。

3
Quassnoi

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

2
flq