SQLは、手続き型言語ではなく、宣言型言語として設計されました。したがって、クエリオプティマイザはすべきではありません それらを適用する方法を決定する際には、where句の述語の順序を考慮してください。
私はおそらく、SQLクエリオプティマイザに関する以下の説明を過度に単純化するつもりです。私は1年前にこれらの線に沿って書いた(それはとても楽しかった!)。最新のクエリ最適化を本当に掘り下げたい場合は、DanTowの
単純なSQLクエリオプティマイザでは、SQLステートメントは最初に関係代数のツリーにコンパイルされます。 オペレーション。これらの操作はそれぞれ、1つ以上のテーブルを入力として受け取り、別のテーブルを出力として生成します。 スキャン データベースからテーブルを読み込むシーケンシャルスキャンです。 並べ替え ソートされたテーブルを生成します。 選択 ある選択条件に従って別のテーブルから行が選択されるテーブルを生成します。 プロジェクト 別のテーブルの特定の列のみを含むテーブルを生成します。 クロス積 2つのテーブルを取り、それらの行の考えられるすべてのペアで構成される出力テーブルを生成します。
紛らわしいことに、SQLSELECT句は関係代数プロジェクトにコンパイルされます。 、WHERE句は関係代数に変わります Select 。 FROM句は1つ以上の結合に変わります 、それぞれ2つのテーブルを取り込み、1つのテーブルを生成します。集合の和集合、共通部分、差、およびメンバーシップを含む他の関係代数演算がありますが、これを単純に保ちましょう。
このツリーは本当に最適化する必要があります。たとえば、次の場合:
select E.name, D.name
from Employee E, Department D
where E.id = 123456 and E.dept_id = D.dept_id
500の部門に5,000人の従業員がいる場合、最適化されていないツリーを実行すると、1人の従業員と1つの部門のすべての可能な組み合わせが盲目的に生成されます(クロス積 )次に選択 必要だった1つの組み合わせだけを出します。 スキャン of Employeeは、5,000のレコードテーブル Scanを作成します。 of Departmentは、500レコードのテーブルである Cross Productを作成します。 これら2つのテーブルのうち、2,500,000のレコードテーブルが生成され、 Select on E.idは、その2,500,000レコードテーブルを取得し、必要なレコードを1つを除いてすべて破棄します。
[もちろん、実際のクエリプロセッサは、これらの中間テーブルのすべてをメモリ内で実体化しないようにします。]
したがって、クエリオプティマイザはツリーをウォークし、さまざまな最適化を適用します。 1つは、各選択を分割することです。 選択のチェーンに 、元の選択ごとに1つ のトップレベルの条件、それらと-一緒に編集。 (これは「連言標準形」と呼ばれます。)次に、個々の小さい選択 ツリー内を移動し、他の関係代数演算とマージして、より効率的な演算を形成します。
上記の例では、オプティマイザは最初に Selectをプッシュします E.id=123456で高価なクロス積を下回っています 手術。これは、クロス積を意味します 500行を生成するだけです(その従業員と1つの部門の組み合わせごとに1つ)。次に、トップレベルの選択 for E.dept_id =D.dept_idは、499個の不要な行を除外します。悪くない。
従業員のIDフィールドにインデックスがある場合、オプティマイザはスキャンを組み合わせることができます。 Selectを使用した従業員の割合 E.id =123456で、高速インデックスを作成します Lookup 。これは、5,000ではなく1つのEmployee行のみがディスクからメモリに読み込まれることを意味します。物事は見上げています。
最後の主要な最適化は、 Selectを選択することです。 E.dept_id =D.dept_idで、外積と組み合わせます。 。これにより、関係代数 Equijoin 手術。これだけではあまり効果がありません。ただし、Department.dept_idにインデックスがある場合は、下位レベルのシーケンシャルスキャン Equijoinに給餌する部門の 非常に高速なインデックスに変換できますルックアップ 私たちの1人の従業員の部門の記録の。
あまり最適化されていない場合は、プロジェクトをプッシュする必要があります 操作がダウンします。クエリのトップレベルにE.nameとD.nameだけが必要で、条件にE.id、E.dept_id、D.dept_idが必要な場合は、スキャン 操作は、他のすべての列を使用して中間テーブルを作成する必要がないため、クエリ実行中のスペースを節約できます。ひどく遅いクエリを2つのインデックスルックアップに変えましたが、それ以外はあまりありません。
元の質問にさらに進んで、あなたが持っているとしましょう:
select E.name
from Employee E
where E.age > 21 and E.state = 'Delaware'
最適化されていない関係代数ツリーを実行すると、5,000人の従業員がスキャンされ、たとえば、21歳以上のデラウェア州の126人の従業員が生成されます。クエリオプティマイザは、データベースの値についても大まかな考えを持っています。 E.state列には、会社が拠点を置いている14の州と、E.ageの分布に関する情報が含まれていることを知っているかもしれません。したがって、最初に、いずれかのフィールドにインデックスが付けられているかどうかを確認します。 E.stateがそうである場合、そのインデックスを使用して、最後に計算された統計に基づいて、クエリプロセッサがデラウェアにいると疑われる少数の従業員を選択することは理にかなっています。 E.ageだけの場合、全従業員の96%が22歳以上であるため、クエリプロセッサはそれが価値がないと判断する可能性があります。したがって、E.stateにインデックスが付けられている場合、クエリプロセッサは Selectを解除します。 E.state='Delaware'をScanとマージします それをはるかに効率的なインデックススキャンに変換します 。
この例では、E.stateとE.ageにインデックスがないとします。結合された選択 操作は、従業員の順次「スキャン」の後に行われます。 選択のどの条件に違いがありますか 最初に行われますか?おそらく大したことではありません。クエリプロセッサは、SQLステートメントでそれらを元の順序のままにする場合もあれば、もう少し洗練されて予想される費用を調べる場合もあります。統計から、E.state ='Delaware'条件はより高度に選択的である必要があることが再びわかります。したがって、条件を逆にして最初にそれを実行し、5,000ではなく126のE.age>21の比較のみが行われるようにします。 。または、文字列の等式の比較は整数の比較よりもはるかにコストがかかり、順序をそのままにしておくことに気付くかもしれません。
いずれにせよ、これはすべて非常に複雑であり、構文条件の順序が違いを生む可能性はほとんどありません。実際のパフォーマンスの問題があり、データベースベンダーが条件の順序をヒントとして使用しない限り、心配する必要はありません。