Quand dois-je utiliser une variable de table par rapport à une table temporaire dans le serveur SQL?

298

J'apprends plus de détails dans la variable de table. Il indique que les tables temporaires sont toujours sur le disque et que les variables de table sont en mémoire, c'est-à-dire que les performances de la variable de table sont meilleures que la table temporaire car la variable de table utilise moins d'opérations d'E / S que la table temporaire.

Mais parfois, s'il y a trop d'enregistrements dans une variable de table qui ne peuvent pas être contenus en mémoire, la variable de table sera placée sur le disque comme la table temporaire.

Mais je ne sais pas ce que sont les "trop ​​de disques". 100 000 enregistrements? ou 1 000 000 enregistrements? Comment savoir si une variable de table que j'utilise est en mémoire ou sur disque? Existe-t-il une fonction ou un outil dans SQL Server 2005 pour mesurer l'échelle de la variable de table ou pour me faire savoir quand la variable de table est placée sur le disque à partir de la mémoire?

yman
la source
5
Une variable de table est presque toujours dans 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.
marc_s
Vous pouvez trouver cette stackoverflow.com/questions/27894/…
Igor Borisenko
2
@marc_s - Vous pouvez supprimer le "presque" dans cette déclaration. Il est toujours dans tempdb(mais peut aussi être entièrement en mémoire)
Martin Smith
2
Avec SQL 2014, vous pouvez maintenant créer une variable de table en mémoire
paparazzo

Réponses:

362

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 #temptable 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).

  1. 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 #temporarytable. 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 UNIQUEou 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. INCLUDECependant, il n'est toujours pas possible de créer des index avec des colonnes -d ou des index columnstore.

  2. Si vous allez ajouter et supprimer plusieurs fois de nombreuses lignes du tableau, utilisez un #temporarytableau. Cela prend en charge TRUNCATE(ce qui est plus efficace que DELETEpour les grandes tables) et les insertions ultérieures suivant un TRUNCATEpeuvent avoir de meilleures performances que celles qui suivent un DELETE comme illustré ici .

  3. 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).
  4. Si le plan optimal utilisant le tableau variera en fonction des données, utilisez un #temporarytableau. 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).
  5. 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).
  6. Si la source des données insérées dans la table provient d'une SELECTinstruction potentiellement coûteuse , considérez que l'utilisation d'une variable de table bloquera la possibilité de cette utilisation d'un plan parallèle.
  7. 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.
  8. Lorsque vous utilisez une #temptable 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 tempdbjournal des transactions jusqu'à ce que le la transaction utilisateur se termine. Cela pourrait donc favoriser l'utilisation de variables de table.
  9. 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 #temporarytables. Bob Ward souligne dans sa tempdbpré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
Martin Smith
la source
2
Bonjour, monsieur Martin Smith. Dans le cas mi, je veux juste stocker un ensemble de valeurs Ids pour les utiliser dans d'autres requêtes à l'intérieur de la procédure Store. Alors que me recommandez-vous?
Jeancarlo Fontalvo
@JeancarloFontalvo - une variable de table avec une clé primaire activée idet l'utilisation de OPTION (RECOMPILE)serait probablement très bien pour cela - mais testez les deux.
Martin Smith
le conflit de métadonnées est-il le même pour la table temporaire et la variable de table?
Syed Aqeel Ashiq
@Syed. Généralement moins pour la télévision. Les verrous peuvent être libérés plus tôt s'ils se trouvent dans une transaction utilisateur. Voir également le lien Bob Ward.
Martin Smith
73

Utilisez une variable de table si pour une très petite quantité de données (milliers d'octets)

Utiliser une table temporaire pour beaucoup de données

Une autre façon de penser: si vous pensez que vous pourriez bénéficier d'un index, de statistiques automatisées ou de tout optimiseur SQL, votre ensemble de données est probablement trop volumineux pour une variable de table.

Dans mon exemple, je voulais simplement mettre environ 20 lignes dans un format et les modifier en tant que groupe, avant de les utiliser pour METTRE À JOUR / INSÉRER une table permanente. Une variable de table est donc parfaite.

Mais j'exécute également SQL pour remplir des milliers de lignes à la fois, et je peux certainement dire que les tables temporaires fonctionnent bien mieux que les variables de table.

Ce n'est pas différent de la façon dont les CTE sont un problème pour une raison de taille similaire - si les données dans le CTE sont très petites, je trouve qu'un CTE fonctionne aussi bien ou mieux que ce que l'optimiseur propose, mais s'il est assez grand, alors ça vous fait très mal.

Ma compréhension est principalement basée sur http://www.developerfusion.com/article/84397/table-variables-v-temporary-tables-in-sql-server/ , qui a beaucoup plus de détails.

Abaque
la source
La valeur à emporter est variable de table est bien pour un petit ensemble de données, mais utilisez la table temporaire pour un plus grand ensemble de données. J'ai une requête avec des milliers de lignes. En passant de la variable de table à la table temporaire, le temps de requête passe de 40 secondes à seulement 5 secondes, toutes choses étant égales par ailleurs.
liang
42

Microsoft dit ici

Les variables de table n'ont pas de statistiques de distribution, elles ne déclencheront pas de recompilation. Par conséquent, dans de nombreux cas, l'optimiseur crée un plan de requête en supposant que la variable de table n'a pas de lignes. Pour cette raison, vous devez être prudent lorsque vous utilisez une variable de table si vous prévoyez un plus grand nombre de lignes (supérieur à 100). Les tables temporaires peuvent être une meilleure solution dans ce cas.

Paul Sturm
la source
14

Je suis totalement d'accord avec Abacus (désolé - je n'ai pas assez de points pour commenter).

En outre, gardez à l'esprit que cela ne se résume pas nécessairement au nombre d' enregistrements dont vous disposez, mais à la taille de vos enregistrements.

Par exemple, avez-vous pris en compte la différence de performances entre 1 000 enregistrements avec 50 colonnes chacun et 100 000 enregistrements avec seulement 5 colonnes chacun?

Enfin, vous interrogez / stockez peut-être plus de données que vous n'en avez besoin? Voici une bonne lecture des stratégies d'optimisation SQL . Limitez la quantité de données que vous extrayez, surtout si vous n'utilisez pas tout (certains programmeurs SQL deviennent paresseux et sélectionnent tout, même s'ils n'utilisent qu'un minuscule sous-ensemble). N'oubliez pas que l'analyseur de requêtes SQL peut également devenir votre meilleur ami.


la source
4

La table variable n'est disponible que pour la session en cours, par exemple, si vous avez besoin d' EXECune autre procédure stockée dans la session en cours, vous devrez passer la table au fur Table Valued Parameteret à mesure que cela affectera les performances, avec les tables temporaires, vous ne pouvez le faire qu'avec passer le nom de la table temporaire

Pour tester une table temporaire:

  • Éditeur de requêtes Open Management Studio
  • Créer une table temporaire
  • Ouvrez une autre fenêtre de l'éditeur de requête
  • Sélectionnez dans ce tableau "Disponible"

Pour tester une table de variables:

  • Éditeur de requêtes Open Management Studio
  • Créer une table variable
  • Ouvrez une autre fenêtre de l'éditeur de requête
  • Sélectionnez dans ce tableau "Non disponible"

quelque chose d'autre que j'ai vécu est: Si votre schéma n'a pas le GRANTprivilège de créer des tables, utilisez des tables variables.

Mina Gabriel
la source
3

en écrivant des données dans des tables déclarées declare @tbet après avoir rejoint d'autres tables, j'ai réalisé que le temps de réponse par rapport aux tables temporaires tempdb .. # tbest beaucoup plus élevé.

Lorsque je les rejoins avec @tb, le temps est beaucoup plus long pour retourner le résultat, contrairement à #tm , le retour est presque instantané.

J'ai fait des tests avec une jointure de 10 000 lignes et joint avec 5 autres tables

César Augusto
la source
Pourriez-vous publier le test que vous avez effectué pour obtenir ces chiffres?
Dan Def