回答:
これは少し広いですが、私は本当の質問を理解しており、それに応じて答えると思います。ただし、テーブルとインデックススプールについてだけ説明します。テーブルスプールとインデックススプールのどちらかを選択するものとして表示するのはまったく正しいとは思いません。ご存知のように、単一のサブツリーで、インデックススプール、テーブルスプール、またはインデックススプールとテーブルスプールの両方を取得することができます。一般的に、次の条件でインデックススプールを取得するのが正しいと考えています。
これらのほとんどは、簡単なデモで見ることができます。一対のヒープを作成することから始めます。
DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_901;
CREATE TABLE dbo.X_10000_VARCHAR_901 (ID VARCHAR(901) NOT NULL);
INSERT INTO dbo.X_10000_VARCHAR_901 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
DROP TABLE IF EXISTS dbo.X_10000_VARCHAR_800;
CREATE TABLE dbo.X_10000_VARCHAR_800 (ID VARCHAR(800) NOT NULL);
INSERT INTO dbo.X_10000_VARCHAR_800 WITH (TABLOCK)
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM master..spt_values t1
CROSS JOIN master..spt_values t2;
最初のクエリでは、検索するものは何もありません。
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
CROSS JOIN dbo.X_10000_VARCHAR_901 b
OPTION (MAXDOP 1);
したがって、オプティマイザーが結合を適用に変換する理由はありません。コストがかかるため、テーブルスプールになります。したがって、このクエリは最初のテストに失敗します。
次のクエリでは、オプティマイザーに適用を検討する理由があると期待するのは当然です。
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID = b.ID
OPTION (LOOP JOIN, MAXDOP 1);
しかし、次のことを意図したものではありません。
このクエリは2番目のテストに失敗します。完全な説明はこちらです。最も関連性の高い部分を引用:
オプティマイザーは、適用を可能にするためにその場で索引を作成することを考慮しません。むしろ、イベントのシーケンスは通常逆です。つまり、適切なインデックスが存在するために適用する変換です。
オプティマイザーが適用を検討するように、クエリを書き直すことができます。
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
INNER JOIN dbo.X_10000_VARCHAR_901 b ON a.ID >= b.ID AND a.ID <= b.ID
OPTION (MAXDOP 1);
しかし、まだインデックススプールはありません。
このクエリは3番目のテストに失敗します。SQL Server 2014では、インデックスキーの長さが900バイトに制限されていました。これはSQL Server 2016で拡張されましたが、非クラスター化インデックスのみが対象です。スプールのインデックスはクラスター化インデックスであるため、制限は900バイトのままです。いずれにせよ、クエリの実行中にエラーが発生する可能性があるため、インデックススプールルールは適用できません。
データ型の長さを800に減らすと、プランにインデックススプールが最終的に提供されます。
インデックススプールプランは、驚くことではありませんが、スプールのないプランよりも大幅に安くなっています。89.7603単位と598.832単位です。文書化されていないQUERYRULEOFF BuildSpool
クエリヒントとの違いを確認できます。
これは完全な答えではありませんが、あなたが探していたものの一部であることを願っています。