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 complète sur le site DBA en examinant les différences entre les deux types d'objet. Cela répond également à votre question sur le disque par rapport à la mémoire (je n'ai pas vu de différence de comportement significative entre les deux).
En ce qui concerne la question dans le titre, cependant, 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 est uniquement possible d'utiliser une variable de table et si vous avez besoin d'écrire dans la table dans une portée enfant, seule une #temp
table fera l'affaire (les paramètres de valeur de table autorisent l' accès en lecture seule ).
Lorsque vous avez le choix, certaines suggestions sont 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'une #temporary
table. Cependant, les détails dépendent de la version. Pour SQL Server 2012 et versions antérieures, les seuls index pouvant être créés sur des variables de table étaient ceux créés implicitement via une contrainte UNIQUE
ou PRIMARY KEY
. SQL Server 2014 a introduit la syntaxe d'index en ligne pour un sous-ensemble des options disponibles dans CREATE INDEX
. Cela a été étendu depuis pour permettre des conditions d'index filtrées. INCLUDE
Cependant, il n'est toujours pas possible de créer des index avec des colonnes -d ou des index columnstore.
Si vous allez ajouter et supprimer plusieurs fois de nombreuses lignes du tableau, utilisez un #temporary
tableau. Cela prend en charge TRUNCATE
(ce qui est plus efficace que DELETE
pour les grandes tables) et les insertions ultérieures suivant un TRUNCATE
peuvent avoir de meilleures performances que celles qui suivent un DELETE
comme illustré ici .
- Si vous supprimez ou mettez à jour un grand nombre de lignes, la table temporaire peut bien mieux fonctionner 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 variera en fonction des données, utilisez un
#temporary
tableau. Cela prend en charge la création de statistiques qui permet 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 ignorer les frais généraux de création et de recompilation des statistiques (il faudrait peut-être des astuces pour fixer le plan que vous souhaitez).
- Si la source des données insérées dans la table provient d'une
SELECT
instruction potentiellement coûteuse , considérez que l'utilisation d'une variable de table bloquera la possibilité de cette utilisation d'un plan parallèle.
- Si vous avez besoin des données de la table pour survivre à une restauration d'une transaction utilisateur externe, utilisez une variable de table. Un cas d'utilisation possible pour cela pourrait consigner la progression de différentes étapes dans un lot SQL long.
- Lorsque vous utilisez une
#temp
table dans un utilisateur, les verrous de transaction peuvent être conservés plus longtemps que pour les variables de table (potentiellement jusqu'à la fin de la transaction vs fin de l'instruction en fonction du type de verrouillage et du niveau d'isolement) et cela peut également empêcher la troncature du tempdb
journal des transactions jusqu'à ce que le la transaction utilisateur se termine. 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 mises en cache est inférieure à celle des
#temporary
tables. Bob Ward souligne dans sa tempdb
présentation que cela peut provoquer des conflits supplémentaires sur les tables système dans des conditions de concurrence élevée. De plus, lorsque vous traitez de petites quantités de données, cela peut faire une différence mesurable dans les performances .
Effets du partage d'ensemble 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
tempDB
- que "en mémoire" est un mythe. Aussi: les variables de table seront toujours considérées par l'optimiseur de requêtes comme contenant exactement une ligne - si vous en avez beaucoup plus, cela peut conduire à de mauvais plans d'exécution.