SQL におけるテーブル スキャンとインデックス スキャンの違いは何ですか?また、具体的にはどこで使用されますか?
4 に答える
ほとんどのクエリエンジンには、効果的なクエリ実行戦略を生成しようとするクエリオプティマイザがあります。インデックスが使用可能な場合、クエリを高速化できます。クエリオプティマイザは、インデックススキャンまたはインデックスシークを実行します。それ以外の場合は、テーブルスキャンを実行します。
例:
SELECT * FROM tbl WHERE category_id = 5;
category_idにインデックスがない場合は、テーブルスキャンが実行されます。つまり、テーブル内のすべてのレコードが正しいcategory_idについて検査されます。
ただし、category_idにインデックスを付けると、状況はさらに複雑になります。テーブルが非常に大きい場合は、インデックスシークが選択される可能性があります。ただし、テーブルが小さい場合、インデックスにアクセスするにはある程度のオーバーヘッドが必要になるため、オプティマイザはテーブルスキャンがさらに高速であると判断する可能性があります。category_idの選択性が十分でない場合、たとえばカテゴリが2つしかない場合は、大きなテーブルの場合でもテーブルのスキャンが高速になる可能性があります。
インデックスは通常、ツリー構造として編成されます。ツリー内のアイテムの検索は、O(log n)操作です。テーブルスキャンはO(n)操作です。速度は主に、クエリの実行に必要なディスクアクセスの数によって決まります。最初にインデックスを検索してから、見つかったエントリのテーブルにアクセスすると、小さなテーブルに対してより多くのディスクアクセスが生成される可能性があります。
別のクエリを見てみましょう。
SELECT category_id FROM tbl WHERE category_id BETWEEN 10 AND 100;
ここに利用可能な別のオプションがあります。この状況では、インデックスシークはテーブルスキャンよりも高速ではない可能性がありますが、catergory_idのみを取得しているため、インデックススキャン(インデックスシークではない)はさらに高速になる可能性があります。インデックススキャンは、ツリー構造(インデックスシークが行うこと)を利用する代わりに、インデックステーブルのすべてのエントリを読み取ります。ただし、要求された情報は完全にインデックスに含まれているため、データテーブルにアクセスする必要はありません。インデックススキャンは、テーブルスキャンのO(n)操作と同様ですが、インデックスは通常テーブルよりも小さいため、テーブルをスキャンするよりもインデックスをスキャンするために必要なディスクアクセスが少なくて済みます。
全体の問題は非常に複雑で、データベースエンジンに大きく依存します。詳細については、dbベンダーが提供するドキュメントをお読みください。
テーブル スキャンとは、すべてのテーブル行を反復処理することを意味します。
インデックス スキャンとは、すべてのインデックス アイテムを反復処理することを意味し、アイテム インデックスが検索条件を満たしている場合、インデックスを介してテーブル行が取得されます。
インデックスはテーブルよりもフラットであるため、通常、インデックス スキャンはテーブル スキャンよりも低コストです。
彼らはこの問題について多くの参考文献です。サンプル:
- Microsoft:インデックス アクセスとテーブル スキャンのどちらが速いですか? :
インデックス アクセスは、SQL Server が既存のインデックスを使用してデータ ページを読み書きするアクセス方法です。インデックス アクセスは I/O 読み取り操作の数を大幅に削減するため、多くの場合、テーブル スキャンよりもパフォーマンスが優れています。
- Oracle:クエリ オプティマイザー
このメソッドでは、ステートメントで指定されたインデックス付きの列の値を使用して、インデックスをトラバースすることによって行が取得されます。インデックス スキャンは、インデックス内の 1 つ以上の列の値に基づいて、インデックスからデータを取得します。索引スキャンを実行するために、Oracle は索引を検索して、ステートメントによってアクセスされる索引付けされた列の値を探します。文が索引の列のみにアクセスする場合、Oracleは、索引付けされた列の値を表からではなく、索引から直接読み取ります。
- MySql:テーブル スキャンを回避する方法
@danihpが質問の最初の部分に答えたので、2番目の「具体的にどこで使用されますか」に答えようとします。これは Oracle の場合ですが、ほとんどの RDBMS に当てはまります。
my_table
列に一意にインデックスが付けられ、列id
に一意ではない 2 番目のインデックスがある tableがあるとしますyet_another_column
。
create my_table ( id varchar2(20) not null
, another_column not null
, yet_another_column
, constraint pk_my_table primary key (id)
);
create index i_my_table on my_table ( yet_another_column );
ここで、これを行う場合は、 index の一意のインデックス スキャンselect * from my_table where id = '1'
を実行する必要があります。次に、インデックスを使用してテーブルに再入力し、すべてをwhereに返します。pk_my_table
my_table
id = '1'
代わりにクエリがあった場合、select id from my_table where id = 'a'
必要なすべての値がインデックス内に含まれているため、2 番目のステージは必要ありません。この場合、クエリは一意のインデックス スキャンのみを実行します。
次に、クエリの場合select * from my_table where yet_another_column = 'y'
、列にインデックスがありますが、一意ではないため、インデックス全体を調べて、 where 条件に一致するすべての値を見つけようとする必要があります。つまり、インデックス スキャンです。ここでも、インデックスにない列を選択しているため、それらを取得するにはテーブルに再入力する必要があります。
最後に、クエリがselect id from my_table where another_column = 'yes'
. インデックスがないため、値を見つけるためにテーブル スキャンanother_column
を実行する必要があります。つまり、テーブル内のすべてを見つける必要があります。where another_column = 'yes'
現在、これらのインスタンスでは、テーブル スキャンとインデックス スキャンの間に大きな違いはないように見えるかもしれません。データベース内のオブジェクトの値を見つける必要があります。ただし、インデックスははるかに小さく、スキャンするように特別に設計されているため(他の回答を参照)、テーブル内の行のごく一部のみが必要な場合は、通常、インデックススキャンを実行する方がはるかに高速です。テーブルの 10% と言う場合、このポイントは「依存する」になります。
少なくとも SQL Server の場合:
テーブル (またはクラスター化インデックス) スキャンではすべてのデータを読み取る必要があるのに対し、おそらくインデックスはテーブル内の列のセット全体をカバーしていないため、インデックス スキャンの方が高速になる可能性があります。インデックスにテーブル内のすべての列が含まれている場合、それはテーブル スキャンとほぼ同等であり、インデックス スキャンとテーブル (または CIX) スキャンの間の選択はコイントスになります。違いは、インデックス内の列が少ない場合、8kb ページにより多くのインデックス行を収めることができるため、インデックス内のすべてのデータをスキャンするために読み取る必要があるページ全体が少なくなるということです。
言いたいことを説明するために、電話帳のコピーが 2 つあるとします。1 つは姓、名、番地、電話番号が記載されており、もう 1 つは姓、名、電話番号のみが記載されています。ここで、住所を印刷する必要がないため、電話帳の任意のページに名前と電話番号の列を 2 つ追加できると想像してください。この結果、同じ数の電話番号をより少ないページに収めることができるため、電話帳が薄くなります。次に、本の電話番号の数を数えることを課されていると想像してください。住所がリストされているもの (テーブル スキャンに類似したページ数の多いもの) と、住所のないもの (ほとんどのインデックス スキャンに類似したページ数の少ないもの) のどちらを選択しますか? ページ数が少ない方を選びます。
これに関するもう 1 つの欠点は、一部のインデックスをフィルター処理できることです。つまり、ほとんどの場合、列数が少ない (したがって、1 ページにより多くの行を収めることができる) だけでなく、多くの行を削除する WHERE 句を使用することもできます。行。この場合も、インデックス スキャンはテーブル スキャンよりも優れています (ただし、これは、WHERE 句が一致し、セマンティクスが同じクエリに対してのみ機能します)。