2015-12-02 11 views
5

私は面白い面接を最近行ったことがあります。そこでは、スカラーの長いリスト(何千もの値)を含むWHERE..IN句を使用してクエリを最適化することについての質問がありました。この質問は、IN句のサブクエリに関するものではなく、スカラの簡単なリストについてです。最適化:WHERE x IN(1,2、.. 100.000)とINNER JOIN tmp_table USING(x)?

これは、INNER JOINを使って、スカラーだけを含む別のテーブル(おそらく一時的なもの)を使って最適化できるとすぐ答えました。私の答えは受け入れられ、査読者から「データベースエンジンは現時点では実行可能な状態で十分に長い時間を最適化することはできません」というメモがありました。私はうなずきました。

しかし、私が歩いて行ったとき、私は疑問を持ち始めました。この状態は、現代のRDBMSにとっては、それを最適化することができないほどには些細で広く使われていたようです。だから、私はいくつかの掘り出しを開始しました。

のPostgreSQL:

それはsortedあるPostgreSQLのparse scalar IN() constructions into ScalarArrayOpExpr structure、こと、らしいです。この構造体は後でインデックススキャン中に一致する行の位置を特定するために使用されます。このようなクエリの場合、EXPLAIN ANALYZEは1つのループしか表示しません。結合は行われません。だから、私はそのようなクエリがINNER JOINよりも速くなると期待しています。私は既存のデータベースでいくつかのクエリを試してみました。しかし、私はテストの純度を気にせず、Postgresはバグダントの下にあったので間違っているかもしれません。

MSSQLサーバー:

MSSQLサーバーbuilds a hash structure from the list of constant expressions and then does a hash join with the source table。並べ替えが行われていないようだが、それはパフォーマンスマッチだと私は思う。このRDBMSの経験がないので、私はテストをしませんでした。

MySQLサーバ:

The 13th of these slidesは5.0前に、この問題は確かにいくつかのケースでMySQLで起こったことを、述べています。しかし、それ以外には、私は悪いIN()治療に関連する他の問題は見つかりませんでした。私は残念ながら逆の証拠を見いだせませんでした。あなたがしたら、私を蹴ってください。

のSQLite:

Documentation pageは、いくつかの問題をヒントが、私は物事は概念レベルで実際にそこにある説明と信じる傾向にあります。その他の情報は見つかりませんでした。

私はインタビュアーを誤解したり、Googleを誤用していると思っています;それは、条件を設定していないし、話が少し曖昧になったからですRDBMSまたは他の条件。それはちょうど抽象的な話だった)。

それはずっと前にあるデータベースが(ところで、リストにNULL値で時々問題を引き起こす可能性があります)OR文のセットとしてIN()を書き直した日、のように見えます。か否か?

もちろん、スカラーのリストが許可されたデータベースプロトコルパケットよりも長い場合は、INNER JOINしか利用できない可能性があります。

私はいくつかのケースでは、(それが準備されていない場合)だけでパフォーマンスを殺すことができるクエリの解析時間だと思う。

また、データベースでは、何度も何度も再解析を行うようになるIN(?)クエリを準備できない可能性があります(パフォーマンスが低下する可能性があります)。実際には、私は試したことはありませんが、このような場合でもクエリの解析とプランニングはクエリの実行と比較して巨大ではないと思います。

しかし、それ以外の問題は他にはありません。まあ、この問題を抱えているだけの問題以外。内部に数千のIDを含むクエリがある場合、アーキテクチャに何か問題があります。

はあなたですか?

+0

はINパラメータの大ammountsに問い合わせプランナのタイムアウトを取得しています。 –

+0

興味深いですが、このサイトには適していません。あなたはそれを知っています...私は閉じようとしています。 –

+0

私は[This thing](http://stackoverflow.com/a/34015333)に乱数と関連があったと書いています。私はそれをエンドツーエンドでやった。付録Cではリストで大きなものを使用しています。千要素。結果は2分の1秒です。 – Drew

答えて

1

あなたの回答は、リストが本当に小さい場合を除き、リストにインデックス(好ましくはプライマリキーインデックス)を作成する場合にのみ正しいです。

最適化の任意の説明は間違いなくデータベース固有のものです。しかし、MySQLは、それがinを最適化する方法については非常に具体的である:

すべての値が定数である場合は1を返しexprがINリスト内の値のいずれかと等しい場合、他の 0を返します。が、彼らは に基づいて評価していますexprの型にソートされます。アイテムの検索は、 バイナリ検索を使用して行われます。これは、IN値が のIN値が完全に定数で構成される場合、INが非常に迅速であることを意味します。

これは間違いなく、INを使用すると、別のテーブルを使用するよりも高速になります。主キーインデックスを使用する別のテーブルよりも高速です。

INをSQL ServerがORのリストに置き換えていると思います。これらは順次比較として実装されます。いくつかの要素が他の要素よりはるかに一般的であり、それらの要素がリストの最初に現れる場合、逐次比較はバイナリ検索よりも高速になる可能性があることに注意してください。私の経験のSQL Serverから

-1

私はそれが悪いアプリケーションの設計だと思います。 IN演算子を使用している値は、たぶんハードコードされていませんが、動的です。そのような場合には、準備されたステートメントをSQLインジェクションを防ぐための唯一の信頼できるメカニズムとして常に使用する必要があります。 いずれの場合も、プレースホルダの数が動的であるため、準備済みの文を動的に書式設定する結果となります。また、過度のハード解析が発生します(INの値 - IN (?)IN (?,?)、 ..)。 これらの値は、前述のようにuse joinとしてこれらの値をロードするか(ロードがオーバーヘッドでない限り)、またはOracleパイプライン関数IN foo(params)を使用します。params引数はメモリから来る複雑な構造(配列)(PLSQL/Javaなど) 値の数が多い場合は、INの代わりにEXISTS (select from mytable m where m.key=x.key)またはEXISTS (select x from foo(params)を使用することを検討します。このような場合、EXISTSは、INよりも優れたパフォーマンスを提供します。

+0

_私はアプリケーション設計が悪いと思っています_答えはすべて質問に接しています。 –

+0

私の答えはおそらく実際には答えられていないので、おそらく元の質問へのコメントとして置くのがよいでしょう。私はVladislavの文章に完全に同意します。「クエリがあると、内部に数千ものIDが含まれていますが、アーキテクチャに何か問題があります。つまり、SQL言語の間違った使用を最適化するための無駄な学問的議論になるので、その質問に答える必要はありません。 – rolish

+0

大規模なIDリストを持つINを使用しているかどうかわからないのは、常に悪いアーキテクチャです。私はそれが仕事に依存し、場合によっては必要かもしれないと思う。大部分のケースでは、そのようなケースを回避できるかどうかを慎重に見直す必要があります。 –