必要なのは、SQL を直接実行できる言語に変換することです。これの専門用語は「クエリ プラン」です。クエリ プランは、データベース エンジンが実行する低レベルの操作 (インデックス検索、結合、並べ替えなど) と、操作がどのように組み合わされるかです。
適切なデータベース エンジンであれば、クエリ プランを確認できます。SQL システムの場合、通常は と呼ばれEXPLAIN
ます。お気に入りの DBMS を入手し (お気に入りがない場合は、MySQL や PostgreSQL を含む、適切なオープン ソース DBMS のすべてがそれを実行します)、さまざまなクエリの計画を調べて、「実際の」操作の種類を確認することをお勧めします。 」 システムを実装します。
また、関係代数を調べることをお勧めします。品揃えの豊富なライブラリにアクセスできる場合、データベースに関するまともな教科書にはセクションまたは章がありますが、Google に尋ねると、かなりの数の優れた参考文献が返されます。リレーショナル代数には、理論的に優れているという利点と、低レベルのデータベース操作を使用してそれを実装する「明白な」方法があるという利点があります。認識を超えて変更することになるかもしれませんが、これは良いスタートになるはずです。
それでは、基本的な概要を見てみましょう。最初に関係代数について読んでから読み進めてください。
実装する必要がある主なデータ構造は、タプルのストリームです。ストリーム内の各タプルは異なりますが、形状はすべて同じです。タプルの各フィールドには名前と型があります。クエリ操作は、タプルの 1 つ以上のストリーム (ちなみに、テーブルはタプルのストリームと考えることができます) を取り、タプルの単一のストリームを返します。
次の形式の基本的な SQLSELECT
ステートメントを考えてみましょう。
SELECT fields
FROM table1,table2
WHERE select_conditions, join_conditions
ここでは、 フィールドが定数と比較されるやselect_conditions
などの条件があります。あるテーブルのフィールドを別のテーブルのフィールドと照合する条件です。(当面は、同じテーブルの 2 つのフィールドを比較するケースは無視します。)gender='F'
age > 18
join_conditions
次に、単純なクエリ プランは次のようになります。
s1 := Select(table1, select_conditions_1)
s2 := Select(table2, select_conditions_2)
j := Join(join_conditions, s1, s2)
res := Project(fields, j)
このSelect
操作はタプルのストリームを受け取り、同じ形状のタプルのストリームを返します。条件に一致しないタプルはすべて削除されます。このProject
操作はタプルのストリームを受け取り、異なるタイプのタプルのストリームを返します。フィールドを削除、再配置、または複製するだけです。最後に、Join
操作はタプルの 2 つのストリームを結合し、結合条件に一致する任意の 2 つのタプルを結合します。(データベース結合操作が何であるかを知らない場合は、これを知る必要があります。Google に尋ねて、Unix の「結合」コマンドも調べてください。)
したがって、この場合、s1
は、テーブル 1 に適用される選択条件に一致するテーブル 1 からのタプルを表すタプルのストリームですs2
。ストリームj
はストリームs1
でありs2
、結合条件に従って結合されます。最後に、クエリで言及されているフィールドのみを射影します。
SQL をリレーショナル代数のような中間形式に変換するのは、実際には非常に簡単です。ただし、単純な翻訳は非常に効率が悪い傾向があります。ここでは、テーブル内のすべてのレコードを調べて、一致するものだけを返すことにより、選択操作を実装します。そのため、クエリの構造とテーブルで利用可能な情報の両方に基づいて、クエリを最適化する必要があります。
たとえば、table1
にフィールドage
およびgender
があり、選択条件age > 18
およびがあるとしますgender = 'F'
。さらに、 がフィールドにtable1
インデックス ( と呼ばれるtable1_age_idx
) を持っているが、age
フィールドにインデックスを持っていないとしgender
ます。明らかに、インデックスを使用する必要があります。これを行うには、操作をさらに 2 つの基本的な操作に分割します。
s1a := IndexSelect(table1_age_idx, age > 18)
s2b := FilterSelect(s1a, gender = 'F')
ここでは、選択操作を 2 つに分割しました。最初の select はインデックス クエリを使用して実装されます (select はテーブルではなくインデックス上にあることに注意してください! gender
) 'F'
。
結合の実装は、さまざまな方法で行うことができます (ソートマージ結合とハッシュ結合が一般的です)。どちらが最適かは、クエリとデータベースによって異なります。一部のインデックス (B ツリーなど) はキーでソートされたレコードを返すため、IndexSelect
後で結合するフィールドに対して既に を実行している場合は、ソートが不要なため、おそらくソートマージ結合の方が適しています。ORDER BY
結合フィールドに句がある場合も同様です。
ご覧のとおり、ここで本当に巧妙なことが起こります。実際のクエリ オプティマイザは、計算の一部として、テーブルのサイズと中間タプル ストリームの予想サイズに関する統計を使用します。ここで、コンパイラについて少し知っておくと役に立ちます。