C'est un peu large mais je pense que je comprends la vraie question et je répondrai en conséquence. Je vais juste parler de la table contre la bobine d'index. Je ne pense pas qu'il soit tout à fait correct de considérer qu'il s'agit d'un choix entre les spoules de table et d'index. Comme vous le savez, il est possible dans une seule sous-arborescence d'obtenir une bobine d'index, une bobine de table ou les deux une bobine d'index et une bobine de table. Je pense qu'il est généralement correct de dire que vous obtenez une bobine d'indexation dans les conditions suivantes:
- L'optimiseur de requêtes a une raison de transformer une jointure en application
- L'optimiseur de requêtes effectue réellement la transformation vers l'application
- L'optimiseur de requêtes utilise la règle pour ajouter une bobine d'index (au minimum, la bobine d'index doit être sûre à utiliser)
- Le plan avec la bobine d'index est sélectionné
Vous pouvez voir la plupart de ces derniers avec des démos simples. Commencez par créer une paire de tas:
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;
Pour la première requête, il n'y a rien à chercher sur:
SELECT *
FROM dbo.X_10000_VARCHAR_901 a
CROSS JOIN dbo.X_10000_VARCHAR_901 b
OPTION (MAXDOP 1);
Il n'y a donc aucune raison pour que l'optimiseur transforme la jointure en application. Vous vous retrouvez avec une bobine de table pour des raisons de coût. Cette requête échoue donc au premier test.
Pour la requête suivante, il est juste de s'attendre à ce que l'optimiseur ait une raison d'envisager une application:
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);
Mais ce n'est pas censé être:
Cette requête échoue au deuxième test. Une explication complète est ici . Citant la partie la plus pertinente:
L'optimiseur n'envisage pas de créer un index à la volée pour activer une application; la séquence des événements est plutôt l'inverse: transformer pour appliquer car un bon index existe.
Je peux réécrire la requête pour encourager l'optimiseur à envisager d'appliquer:
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);
Mais il n'y a toujours pas de bobine d'index:
Cette requête échoue au troisième test. Dans SQL Server 2014, il y avait une limite de longueur de clé d'index de 900 octets. Cela a été étendu dans SQL Server 2016, mais uniquement pour les index non cluster. L'index pour un spool est un index clusterisé donc la limite reste à 900 octets . Dans tous les cas, la règle de spoule d'index ne peut pas être appliquée car elle pourrait entraîner une erreur lors de l'exécution de la requête.
La réduction de la longueur du type de données à 800 fournit enfin un plan avec une bobine d'index:
Le plan de bobine d'index, sans surprise, coûte beaucoup moins cher qu'un plan sans bobine: 89,7603 unités contre 598,832 unités. Vous pouvez voir la différence avec l' QUERYRULEOFF BuildSpool
indice de requête non documentée :
Ce n'est pas une réponse complète, mais j'espère que c'est une partie de ce que vous cherchiez.