インデックスの使用量
クエリ エンジンが、クエリ フィルターを評価してから、コンテナーのインデックスを走査します。 クエリ エンジンは、フィルターを評価する次の 3 つの方法から最も効率的なものを自動的に使用しようとします。
方法 | 説明 | RU との関係 |
---|---|---|
インデックス シーク | クエリ エンジンが、フィールド値との完全一致をシークするために、その値を直接走査し、一致する項目がいくつあるかを調べます。 一致する項目を判別すると、クエリ エンジンが項目をクエリ結果として返します。 | RU 料金は検索について一定です。 項目を読み込んで返すための RU 料金は、項目数に比例します。 |
インデックス スキャン | クエリ エンジンは、フィールドについて可能性があるすべての値を見つけ、その後でそれらの値に対してのみさまざまな比較を実行します。 一致が見つかると、クエリ エンジンが項目を読み込み、クエリ結果として返します。 | RU 料金は検索についてはこの場合も一定ですが、インデックス付きプロパティのカーディナリティに基づいて、インデックス シークよりも少し高くなります。 項目を読み込んで返すための RU 料金は、やはり返される項目数に比例します。 |
フル スキャン | クエリ エンジンは、項目全体をトランザクション ストアに読み込んでフィルターを評価します。 | この種類のスキャンでは、インデックスは使用されません。ただし、項目を読み込むための RU 料金は、コンテナー全体の項目数に基づきます。 |
注意
効率的で正確なインデックス スキャンや、入り組んで拡張されたインデックス スキャンから、最終的には最も複雑なフル インデックス スキャンまで、インデックス スキャンの複雑さには幅があります。
クエリ開発者は、クエリでシークとスキャンのどちらが必要かを理解することが不可欠です。 また、インデックスを使用できないためフル スキャンが必要なクエリについても理解することが重要です。 具体的には、最も効率的な検索方法を使用するフィルター述語を使用するようにクエリを最適化する必要があります。
プロパティとクエリが検索方法に与える影響について説明します。 この例で、架空のコンテナーに 3 つの項目があり、一意識別子、name 文字列プロパティ、price 数値プロパティが含まれています。
[
{
"id": "1",
"name": "Touring-1000 Blue",
"price": 675.55
},
{
"id": "2",
"name": "Mountain-400-W Silver",
"price": 1215.40
},
{
"id": "3",
"name": "Road-200 Red",
"price": 405.85
}
]
これらの各項目は、ツリーとして視覚化できます。
最初の項目のツリー表現には、Touring-1000 Blue がある name ノードと、子値ノード 675.55 がある price ノードが含まれます。
2 番目の項目のツリーには、それぞれの値が Mountain-400-W Silver と 1215.40 の同じノードが含まれます。
最後の項目のツリーも同様で値が Road-200 Red と 405.85 です。
3 つの項目すべてを含む逆ツリーには、3 つの項目すべてと一致するルート ノードが含まれます。 ルート ノードの走査は、概念上では、フィルターなしのクエリーに似ていると考えることができます。 このツリーには、それぞれ異なる値の 3 つの子ノードを含む price ノードが含まれます。 price ノードは 3 つの項目すべてと一致します。どの項目にも price フィールドが含まれるためです。 ただし、個々の price ノードは 1 つの項目のみと一致します。各項目の price は固有であるためです。 このツリーには、それぞれ個別の値の個々の子ノードを持つ 3 つの項目すべてと一致する name ノードも含まれます。
このツリーを走査するために、name が値 Touring-1000 Blue と等しい場合のみ項目と一致するというフィルターを使用して単純な SQL クエリを記述します。
SELECT
*
FROM
products p
WHERE
p.name = 'Touring-1000 Blue'
このとき、クエリ エンジンは次の順序でツリーを走査します。
- エンジンはルートから始めます。 現時点では、まだすべての項目に一致の可能性があります。
- エンジンは name ノードを走査します。 なお、すべての項目が一致します。
- 最後に、エンジンは完全一致である Touring-1000 Blue ノードを走査します。 この時点では項目 #2 のみが一致します。
- 次に、クエリ エンジンは、項目 #2 の JSON コンテンツ全体を読み込み、結果セットにそれを返します。
このツリー図は、Touring-1000 Blue ノードまでの走査プロセスを示しています。
この走査は、インデックス シーク検索方法の使用例です。 正確な値との実際の照合は、RU の固定料金です。クエリ エンジンは、各項目の JSON コンテンツを検索する代わりにインデックスを使用するためです。 一致する項目が見つかると、クエリ エンジンはクライアント アプリケーションに返すために JSON コンテンツを読み込みます。
クエリ フィルターが既知の値と一致しない場合、結果セットには項目が返されません。 フィールドに対して複数の項目が同じ値を持つ場合、ツリーは複数の項目を返すようにクエリ エンジンに指示します。
インデックス シークのもう 1 つの例は、IN フィルターを使用して、可能性がある複数のフィールドに対して等値照合を実行する SQL クエリです。
SELECT
*
FROM
products p
WHERE
p.name IN ('Road-200 Red', 'Mountain-400-W Silver')
このとき、クエリ エンジンは次の順序でツリーを走査します。
- エンジンはルートから始めます。 現時点では、まだすべての項目に一致の可能性があります。
- エンジンは name ノードを走査します。 なお、すべての項目が一致します。
- 最後に、エンジンは完全一致を走査します。 これには Road-200Red および Mountain-400-W Silver ノードが含まれます。 この時点では、項目 #1 と #3 のみが一致します。
- 次に、クエリ エンジンは、項目 #1 と #3 両方の JSON コンテンツ全体を読み込み、結果セットにそれを返します。
このツリー図は、name ノード内の一致する子値の走査プロセスを示しています。
クエリによっては、さらに複雑なインデックス検索方法が必要となる他の演算子が使用されることがあります。 この例の SQL クエリでは、2 つの範囲比較に基づいて項目がフィルター処理されます。 わかりやすく言うと、このクエリによって price が 500 から 1,000 までの項目が検索されます。
SELECT
*
FROM
products p
WHERE
p.price >= 500 AND
p.price <= 1000
クエリ エンジンは次の順序でツリーを走査します。
- エンジンはルートから始めます。 現時点では、まだすべての項目に一致の可能性があります。
- エンジンは price ノードを走査します。 なお、すべての項目が一致します。
- クエリ エンジンは、可能性があるすべての値に対してバイナリ検索を実行し、フィルターと一致するかどうかを示します。
- 最後に、エンジンは、一致する値を項目にマップします。 675.55 ノードのみが一致します。 その結果、この時点では項目 #1 のみが一致します。
- 次に、クエリ エンジンは、項目 #1 の JSON コンテンツ全体を読み込み、結果セットにそれを返します。
このバイナリ検索によって、複雑さの点で、クエリがインデックス シークから正確なインデックス スキャンに変わりました。 クエリ エンジンは、インデックス内の可能性があるすべての値をスキャンしますが、これはすべての JSON コンテンツのフル スキャンよりもはるかに効率的です。 範囲比較と文字列関数を使用するクエリでは、多くの場合、インデックス スキャンを使用する必要があります。
クエリ エンジンがフィルターの評価にインデックスを使用できないエッジ ケースがあります。 このようなケースでは、クエリ エンジンが、フィルターを評価する前に、すべての項目の JSON コンテンツをトランザクション ストアに読み込む必要があります。 フル スキャンでは、コンテナー内の項目の合計数に比例して料金が上昇するため、高額の要求ユニット料金が発生する可能性があります。 フル スキャンはまれですが、クエリ フィルターで特定の組み込み関数を使用する場合に行われる可能性があることを知っておくことが大切です。