Votre question montre que vous avez succombé à certaines des idées fausses courantes concernant les variables de table et les tables temporaires.
J'ai écrit une réponse assez détaillée sur le site DBA en examinant les différences entre les deux types d'objets. Cela répond également à votre question sur le disque par rapport à la mémoire (je n'ai pas vu de différence significative de comportement entre les deux).
En ce qui concerne la question dans le titre, à savoir quand utiliser une variable de table par rapport à une table temporaire locale, vous n'avez pas toujours le choix. Dans les fonctions, par exemple, il n'est possible d'utiliser qu'une variable de table et si vous devez écrire dans la table dans une portée enfant, alors seulement un #temp
table fera l'affaire (les paramètres de table permettent un accès en lecture seule).
Si vous avez le choix, quelques suggestions sont présentées ci-dessous (bien que la méthode la plus fiable consiste simplement à tester les deux avec votre charge de travail spécifique).
-
Si vous avez besoin d'un index qui ne peut pas être créé sur une variable de table, vous aurez bien sûr besoin d'un
#temporary
table. Les détails de ceci dépendent de la version cependant. Pour SQL Server 2012 et les versions antérieures, les seuls index qui pouvaient être créés sur les variables de table étaient ceux créés implicitement via unUNIQUE
ouPRIMARY KEY
contrainte. SQL Server 2014 a introduit la syntaxe d'index en ligne pour un sous-ensemble des options disponibles dansCREATE INDEX
. Cela a été étendu depuis pour permettre des conditions d'index filtrées. Index avecINCLUDE
Cependant, il n'est toujours pas possible de créer des colonnes -d ou des index columnstore sur des variables de table. -
Si vous ajoutez et supprimez à plusieurs reprises un grand nombre de lignes de la table, utilisez un
#temporary
table. Qui prend en chargeTRUNCATE
(ce qui est plus efficace queDELETE
pour les grandes tables) et en plus les insertions suivantes après unTRUNCATE
peuvent avoir de meilleures performances que celles qui suivent unDELETE
comme illustré ici. - Si vous supprimez ou mettez à jour un grand nombre de lignes, la table temporaire peut bien fonctionner bien mieux qu'une variable de table - si elle est capable d'utiliser le partage d'ensemble de lignes (voir "Effets du partage d'ensemble de lignes" ci-dessous pour un exemple) .
- Si le plan optimal utilisant le tableau varie en fonction des données, utilisez un
#temporary
table. Cela prend en charge la création de statistiques qui permettent au plan d'être recompilé dynamiquement en fonction des données (bien que pour les tables temporaires mises en cache dans les procédures stockées, le comportement de recompilation doit être compris séparément). - S'il est peu probable que le plan optimal pour la requête utilisant la table change, vous pouvez envisager une variable de table pour éviter la surcharge de la création de statistiques et des recompilations (cela pourrait nécessiter des conseils pour corriger le plan souhaité).
- Si la source des données insérées dans la table provient d'un
SELECT
potentiellement coûteux considérez alors que l'utilisation d'une variable de table bloquera la possibilité d'utiliser un plan parallèle. - Si vous avez besoin que les données de la table survivent à l'annulation d'une transaction d'utilisateur externe, utilisez une variable de table. Un cas d'utilisation possible pour cela pourrait être la journalisation de la progression des différentes étapes dans un long lot SQL.
- Lorsque vous utilisez un
#temp
table au sein d'une transaction utilisateur, les verrous peuvent être conservés plus longtemps que pour les variables de table (potentiellement jusqu'à la fin de la transaction par rapport à la fin de l'instruction en fonction du type de verrou et du niveau d'isolement) et peuvent également empêcher la troncation dutempdb
journal des transactions jusqu'à la fin de la transaction utilisateur. Cela pourrait donc favoriser l'utilisation de variables de table. - Dans les routines stockées, les variables de table et les tables temporaires peuvent être mises en cache. La maintenance des métadonnées pour les variables de table en cache est inférieure à celle de
#temporary
les tables. Bob Ward souligne dans sontempdb
présentation que cela peut entraîner des conflits supplémentaires sur les tables système dans des conditions de forte simultanéité. De plus, lorsque vous traitez de petites quantités de données, cela peut avoir un impact mesurable sur les performances.
Effets du partage d'ensembles de lignes
DECLARE @T TABLE(id INT PRIMARY KEY, Flag BIT);
CREATE TABLE #T (id INT PRIMARY KEY, Flag BIT);
INSERT INTO @T
output inserted.* into #T
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY @@SPID), 0
FROM master..spt_values v1, master..spt_values v2
SET STATISTICS TIME ON
/*CPU time = 7016 ms, elapsed time = 7860 ms.*/
UPDATE @T SET Flag=1;
/*CPU time = 6234 ms, elapsed time = 7236 ms.*/
DELETE FROM @T
/* CPU time = 828 ms, elapsed time = 1120 ms.*/
UPDATE #T SET Flag=1;
/*CPU time = 672 ms, elapsed time = 980 ms.*/
DELETE FROM #T
DROP TABLE #T