web-dev-qa-db-fra.com

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

J'apprends plus de détails dans la variable de table. Il dit 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 celles de la table temporaire, car elles utilisent moins d'opérations IO que la table temporaire.

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

Mais je ne sais pas ce qu'est le "trop ​​de disques". 100 000 enregistrements? ou 1000 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 prévenir lorsque la variable de table est mise sur le disque à partir de la mémoire?

275
yman

Votre question montre que vous avez cédé à certaines des idées fausses courantes sur les variables de table et les tables temporaires.

J'ai écrit ne réponse assez détaillée sur le site DBA en regardant 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 constaté de différence significative de comportement entre les deux).

En ce qui concerne la question dans le titre sur le moment où 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 sur la table dans une étendue enfant, alors seule une table #temp fera l'affaire (les paramètres de valeur table autorisent en lecture seule). accès ).

Si vous avez le choix, voici quelques suggestions (même si 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 table #temporary. Les détails de cette opération dépendent toutefois de la version. Pour SQL Server 2012 et ultérieur, 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 autoriser les conditions d'index filtrées. Les index avec INCLUDE- d colonnes ou les index de colonnes ne sont toujours pas possibles pour créer des variables de table.

  2. Si vous souhaitez ajouter et supprimer de manière répétée un grand nombre de lignes de la table, utilisez une table #temporary. Cela prend en charge TRUNCATE (qui est plus efficace que DELETE pour les grandes tables). De plus, les insertions suivantes suivant un TRUNCATE peuvent avoir de meilleures performances que celles qui suivent un DELETEcomme illustré ici .

  3. Si vous souhaitez supprimer ou mettre à jour un grand nombre de lignes, la table temporaire peut fonctionner beaucoup 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).
  4. Si le plan optimal utilisant la table varie en fonction des données, utilisez une table #temporary. 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).
  5. S'il est improbable que le plan optimal de la requête utilisant la table change, vous pouvez envisager une variable de table pour ignorer la surcharge liée à la création et à la recompilation de statistiques (il faudra éventuellement des astuces pour résoudre le plan souhaité).
  6. Si la source des données insérées dans la table provient d'une instruction SELECT potentiellement coûteuse, envisagez l'utilisation d'une variable de table pour bloquer la possibilité d'utiliser un plan parallèle.
  7. Si vous avez besoin que les données de la table survivent à une restauration d'une transaction utilisateur externe, utilisez une variable de table. Un cas d'utilisation possible consiste à consigner la progression de différentes étapes dans un lot SQL long.
  8. Lors de l'utilisation d'une table #temp au sein d'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 par rapport à la fin de l'instruction, en fonction du type de verrouillage et du niveau d'isolation). le journal de transactions tempdb jusqu'à la fin de la transaction utilisateur. 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 en cache est inférieure à celle pour les tables #temporary. Bob Ward souligne dans sa tempdb presentation que cela peut entraîner des conflits supplémentaires sur les tables système dans des conditions de simultanéité élevée. De plus, lorsque vous manipulez de petites quantités de données, cela peut avoir un effet ne différence mesurable sur les performances .

Effets du partage d'un 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
343
Martin Smith

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 pouvoir tirer parti d’un index, de statistiques automatisées ou de tout avantage de l’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. Donc, une variable de table est parfaite.

Mais j'utilise aussi SQL pour remplir de manière aléatoire des milliers de lignes à la fois, et je peux certainement affirmer que les tables temporaires fonctionnent beaucoup mieux que les variables de table.

Ce n'est pas sans rappeler à quel point les ETC sont une source d'inquiétude pour une raison de taille similaire: si les données contenues dans l'ECU sont très petites, je constate qu'un CTE donne des résultats aussi bons, voire meilleurs, que ce que l'optimiseur propose, mais s'il est assez volumineux, ça vous fait mal.

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

67
Abacus

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 construira un plan de requête en supposant que la variable de table ne comporte aucune ligne. Pour cette raison, vous devez être prudent avec l'utilisation d'une variable de table si vous vous attendez à un plus grand nombre de lignes (supérieur à 100). Les tables temporaires peuvent être une meilleure solution dans ce cas.

36
Paul Sturm

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

Aussi, gardez à l'esprit qu'il ne s'agit pas nécessairement de combien enregistrements que vous avez, mais du taille de vos enregistrements.

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

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

14
user3810900

Table de variables n'est disponible que pour la session en cours. Par exemple, si vous devez EXEC une autre procédure stockée dans la procédure actuelle, vous devrez transmettre la table sous la forme Table Valued Parameter et bien sûr, cela affectera les performances, avec tables temporaires vous pouvez le faire en ne transmettant que le nom de la table temporaire

Pour tester une table temporaire:

  • Editeur de requêtes Open Studio Management
  • Créer une table temporaire
  • Ouvrir une autre fenêtre d'éditeur de requête
  • Sélectionnez dans cette table "Disponible"

Pour tester une table de variables:

  • Editeur de requêtes Open Studio Management
  • Créer une table de variables
  • Ouvrir une autre fenêtre d'éditeur de requête
  • Sélectionnez dans cette table "Non disponible"

une autre chose que j'ai expérimentée est la suivante: si votre schéma n'a pas le privilège GRANT de créer des tables, utilisez des tables de variables.

4
Mina Gabriel

écrire des données dans des tables déclarées declare @tb et après avoir rejoint d’autres tables, je me suis rendu compte que le temps de réponse par rapport aux tables temporaires tempdb .. # tb était beaucoup plus élevé.

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

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

3
César Augusto