SQL ステートメントの処理

単一の SQL ステートメントの処理は、SQL Server で SQL ステートメントを実行する最も基本的な方法です。ローカルのベース テーブルだけを参照する (ビューやリモート テーブルは参照しない) 単一の SELECT ステートメントを処理する手順は、基本的な処理の例です。

SELECT ステートメントの最適化

SELECT ステートメントは非手続き型であり、要求したデータを取得するときにデータベース サーバーで使用する手順が細かく指定されません。つまり、データベース サーバーが SELECT ステートメントを分析して、要求したデータを抽出する最も効率的な方法を決定する必要があります。これを、SELECT ステートメントの最適化と呼びます。また、最適化を行うコンポーネントをクエリ オプティマイザーと呼びます。オプティマイザーへの入力は、クエリ、データベース スキーマ (テーブル定義やインデックスの定義)、およびデータベース統計で構成されます。クエリ オプティマイザーの出力が、クエリ実行プランです。これは、クエリ プランや単にプランと呼ばれることもあります。クエリ プランの内容については、このトピックの後半で説明します。

単一 SELECT ステートメントの最適化中のクエリ オプティマイザーの入出力は、次の図のようになります。

SELECT ステートメントの最適化

SELECT ステートメントは次の事項だけを定義します。

  • 結果セットの形式。ほとんどの場合、選択リスト内で指定します。ただし、ORDER BY、GROUP BY などの句も結果セットの最終形式に影響します。

  • 取得するデータが含まれているテーブル。FROM 句で指定します。

  • SELECT ステートメントを実行するためにテーブルを論理的に関連付ける方法。FROM の後に指定する WHERE 句や ON 句などで指定する結合により定義します。

  • 基になるテーブルの行が SELECT ステートメントの対象になるために満たす必要がある条件。WHERE 句と HAVING 句で指定します。

クエリ実行プランは、次の事項を定義しています。

  • 基になるテーブルにアクセスする順序

    通常、データベース サーバーからベース テーブルにアクセスして結果セットを構築する順序は何とおりもあります。たとえば、SELECT ステートメントが 3 つのテーブルを参照している場合、データベース サーバーは最初に TableA にアクセスし、TableA のデータを使用して TableB から一致する行を抽出します。次に、TableB のデータを使用して TableC のデータを抽出することができます。データベースがテーブルにアクセスする際に可能な順序には、次の組み合わせがあります。

    TableCTableBTableA

    TableBTableATableC

    TableBTableCTableA

    TableCTableATableB

  • 各テーブルからデータを取り出す方法

    通常、各テーブルのデータにアクセスする方法にも何とおりかあります。特定のキー値を持つ数行だけが必要な場合、データベース サーバーではインデックスを使用できます。テーブル内のすべての行が必要な場合は、インデックスを無視してテーブル スキャンを実行できます。テーブル内のすべての行が必要で、ORDER BY で指定されたキー列のインデックスがある場合、テーブル スキャンではなくインデックス スキャンを行うと、結果セットの並べ替えを個別に行いません。テーブルが非常に小さい場合は、テーブルにどのようにアクセスするときでもテーブル スキャンが最も効率的な方法です。

可能性のある多数のプラン候補の中から実行プランを 1 つ選択する処理を最適化と呼びます。クエリ オプティマイザーは、SQL データベース システムで最も重要なコンポーネントの 1 つです。クエリ オプティマイザーはクエリの分析やプランの選択を行うため、オーバーヘッドが発生します。ただし、クエリ オプティマイザーが効率的な実行プランを選択すれば、このオーバーヘッドの数倍の負荷を削減することができます。たとえば、2 つの建築会社が一軒の住宅に同じ青写真を提供する場合について考えてみます。一方の会社は数日かけて建築プランを立て、他方の会社はプランを立てずに建築を開始するとします。ほとんどの場合、プロジェクトのプランに時間をかけた会社の方が先に完成します。

SQL Server クエリ オプティマイザーは、コストベースのオプティマイザーです。実行プラン候補ごとに、計算に使用するリソースの量の観点から関連するコストが異なります。クエリ オプティマイザーでは、候補のプランを分析し、算出コストが最も低いプランを選択する必要があります。複雑な SELECT ステートメントの場合、何千もの実行プラン候補があります。このような場合、クエリ オプティマイザーはすべての組み合わせを分析するわけではありません。複雑なアルゴリズムを使用して、理論上の最低コストに最も近い実行プランを迅速に見つけ出します。

SQL Server クエリ オプティマイザーは、リソース コストが最も低い実行プランに限定して選択するわけではありません。妥当なリソース コストで、最も迅速に結果を返すプランを選択します。たとえば、クエリを並列に処理すれば、直列に処理するよりもリソースを多く使用します。ただし、クエリの完了時間は短縮されます。この場合、サーバー側の負荷に悪影響がない限り、並列実行プランを使用して、結果が返されます。

クエリ オプティマイザーは、テーブルやインデックスから情報を取り出す複数の方法のリソース コストを算出する場合、分布統計に大きく依存します。分布統計は、列やインデックスごとに保存されています。この統計情報は、特定のインデックスや列の値の選択度を表しています。たとえば、車を表すテーブルの場合、同メーカーの車がいくつもあります。ただし、VIN (車両番号) はそれぞれの車両固有のものです。VIN のインデックスの方が、メーカーのインデックスよりも選択度が高いと言えます。インデックス統計が最新でない場合、クエリ オプティマイザーはテーブルの現在の状態に対して最適な選択ができないことがあります。インデックス統計を最新の状態に保持する方法の詳細については、「クエリのパフォーマンスを向上させるための統計の使用」を参照してください。

クエリ オプティマイザーを使用すると、プログラマやデータベース管理者が入力しなくても、データベース内の状態の変化に合わせてデータベース サーバーを動的に調整できるので、クエリ オプティマイザーは不可欠です。これにより、プログラマはクエリの最終結果の記述だけに重点を置くことができます。クエリ オプティマイザーは、ステートメントを実行するたびに、データベースの状態に合わせて効率的な実行プランを構築します。

SELECT ステートメントの処理

SQL Server が単一の SELECT ステートメントを処理する基本的な手順は次のとおりです。

  1. パーサーが SELECT ステートメントをスキャンし、キーワード、式、演算子、識別子などの論理単位に分解します。

  2. 基になるデータを結果セットで必要な形式に変換する論理手順を記述するクエリ ツリーが構築されます。クエリ ツリーはシーケンス ツリーとも呼ばれます。

  3. クエリ オプティマイザーでは、基になるテーブルにアクセスできるさまざまな方法が分析されます。その中から、使用するリソースが最も少なく、最も短時間で結果を返す一連の手順が選択されます。クエリ ツリーが更新され、この一連の手順が正確に記録されます。この最終的に得られた最適化済みのクエリ ツリーを実行プランと呼びます。

  4. リレーショナル エンジンによって、実行プランの実行が開始されます。リレーショナル エンジンは、ベース テーブルからのデータを必要とする手順を処理するときに、要求した行セットのデータを渡すようにストレージ エンジンに要求します。

  5. リレーショナル エンジンでは、ストレージ エンジンから返されたデータが結果セット用に定義された形式に変換され、結果セットをクライアントに返します。

その他のステートメントの処理

SELECT ステートメントの処理で説明した基本的な手順は、INSERT、UPDATE、DELETE などの SQL ステートメントにも適用されます。UPDATE ステートメントと DELETE ステートメントは、いずれも変更または削除される行セットを対象とする必要があります。これらの行を特定する処理は、SELECT ステートメントの結果セットで使用された、基になる行を特定する処理と同じです。UPDATE ステートメントと INSERT ステートメントのどちらにも、更新または挿入するデータ値を指定する SELECT ステートメントを埋め込むことができます。

CREATE PROCEDURE、ALTER TABLE などの DDL (データ定義言語) ステートメントも、最終的には、システム カタログ テーブル上の一連のリレーショナル操作になります。また、場合によっては ALTER TABLE ADD COLUMN など、データ テーブルに対する一連のリレーショナル操作になります。