Quelle est la différence entre une table temporaire et une variable de table dans SQL Server?


390

Dans SQL Server 2005, nous pouvons créer des tables temporaires de deux manières:

declare @tmp table (Col1 int, Col2 int);

ou

create table #tmp (Col1 int, Col2 int);

Quelles sont les différences entre ces deux? J'ai lu des opinions contradictoires sur si @tmp utilise toujours tempdb, ou si tout se passe en mémoire.

Dans quels scénarios l'un surpasse-t-il l'autre?



2
Il y a un très bon article écrit par Pinal Dave ici ... blog.sqlauthority.com/2009/12/15/…
sam yi

Réponses:


392

Il existe quelques différences entre les tables temporaires (#tmp) et les variables de table (@tmp), bien que l'utilisation de tempdb n'en soit pas une, comme indiqué dans le lien MSDN ci-dessous.

En règle générale, pour les volumes de données petits à moyens et les scénarios d'utilisation simples, vous devez utiliser des variables de table. (Il s'agit d'une directive trop large avec bien sûr de nombreuses exceptions - voir ci-dessous et les articles suivants.)

Quelques points à considérer lors du choix entre eux:

  • Les tables temporaires sont de vraies tables, vous pouvez donc faire des choses comme CRÉER DES INDEX, etc. Si vous avez de grandes quantités de données pour lesquelles l'accès par index sera plus rapide, les tables temporaires sont une bonne option.

  • Les variables de table peuvent avoir des index à l'aide de contraintes PRIMARY KEY ou UNIQUE. (Si vous voulez un index non unique, incluez simplement la colonne de clé primaire comme dernière colonne dans la contrainte unique. Si vous n'avez pas de colonne unique, vous pouvez utiliser une colonne d'identité.) SQL 2014 a aussi des index non uniques .

  • Les variables de table ne participent pas aux transactions et les SELECTs le sont implicitement NOLOCK. Le comportement de la transaction peut être très utile, par exemple si vous souhaitez faire un ROLLBACK à mi-chemin d'une procédure, alors les variables de table remplies pendant cette transaction seront toujours remplies!

  • Les tables temporaires peuvent entraîner la recompilation des procédures stockées, peut-être souvent. Les variables de table ne le seront pas.

  • Vous pouvez créer une table temporaire à l'aide de SELECT INTO, qui peut être plus rapide à écrire (idéal pour les requêtes ad hoc) et peut vous permettre de faire face aux changements de types de données au fil du temps, car vous n'avez pas besoin de définir la structure de votre table temporaire à l'avance.

  • Vous pouvez renvoyer des variables de table à partir de fonctions, ce qui vous permet d'encapsuler et de réutiliser la logique beaucoup plus facilement (par exemple, rendre une fonction pour diviser une chaîne en une table de valeurs sur un délimiteur arbitraire).

  • L'utilisation de variables de table dans des fonctions définies par l'utilisateur permet d'utiliser ces fonctions plus largement (voir la documentation CREATE FUNCTION pour plus de détails). Si vous écrivez une fonction, vous devez utiliser des variables de table sur des tables temporaires, sauf indication contraire impérative.

  • Les variables de table et les tables temporaires sont stockées dans tempdb. Mais les variables de table (depuis 2005) utilisent par défaut le classement de la base de données actuelle par rapport aux tables temporaires qui prennent le classement par défaut de tempdb ( ref ). Cela signifie que vous devez être conscient des problèmes de classement si vous utilisez des tables temporaires et que votre classement db est différent de celui de tempdb, ce qui pose des problèmes si vous souhaitez comparer les données de la table temporaire avec les données de votre base de données.

  • Les tables temporaires globales (## tmp) sont un autre type de table temporaire disponible pour toutes les sessions et tous les utilisateurs.

Quelques lectures supplémentaires:


26
Les variables de table peuvent avoir des index. Créez simplement une contrainte unique et vous obtenez automatiquement un index. Fait une énorme différence de performances. (Si vous ne voulez pas d'index unique, ajoutez simplement la clé primaire réelle à la fin des champs souhaités. Si vous n'en avez pas, créez une colonne d'identité).
Ben

7
@Ben et SQL Server 2014 permettent de spécifier des
Martin Smith

4
Les variables de table qui ne sont pas affectées par les transactions sont parfois pratiques. Si vous avez quelque chose à conserver après une restauration, vous pouvez le mettre dans une variable de table.
disjoncteur

3
Des statistiques sont créées pour les tables temporaires, ce qui peut améliorer les plans de requête, mais pas pour les variables de table. Ces statistiques sont mises en cache pendant un certain temps, avec les pages de la table temporaire, après la suppression de la table temporaire et peuvent être inexactes si la table mise en cache est réactivée.
Michael Green

Les variables de table seront par défaut soit le classement du type de données défini par l'utilisateur (si la colonne est d'un type de données défini par l'utilisateur) ou le classement de la base de données actuelle et non le classement par défaut de tempdb. Les tables temporaires utiliseront le classement par défaut tempdb. Voir: technet.microsoft.com/en-us/library/ms188927.aspx
PseudoToad

25

Il suffit de regarder l'affirmation dans la réponse acceptée selon laquelle les variables de table ne participent pas à la journalisation.

Il semble généralement faux qu'il existe une différence dans la quantité de journalisation (au moins pour les opérations insert/ update/ deletesur la table elle-même, bien que j'ai depuis constaté qu'il y a une petite différence à cet égard pour les objets temporaires mis en cache dans les procédures stockées en raison de la table système supplémentaire mises à jour).

J'ai examiné le comportement de journalisation par rapport à a @table_variableet à une #temptable pour les opérations suivantes.

  1. Insertion réussie
  2. Insertion sur plusieurs lignes où l'instruction a été annulée en raison d'une violation de contrainte.
  3. Mise à jour
  4. Supprimer
  5. Désallouer

Les enregistrements du journal des transactions étaient presque identiques pour toutes les opérations.

La version de la variable de table contient en fait quelques entrées de journal supplémentaires, car elle obtient une entrée ajoutée (et ultérieurement supprimée de) la sys.syssingleobjrefstable de base, mais dans l'ensemble, quelques octets de moins étaient consignés uniquement, car le nom interne des variables de table consomme 236 octets de moins que pour les #temptables. (118 nvarcharcaractères de moins ).

Script complet à reproduire (meilleure exécution sur une instance démarrée en mode mono-utilisateur et en sqlcmdmode utilisation )

:setvar tablename "@T" 
:setvar tablescript "DECLARE @T TABLE"

/*
 --Uncomment this section to test a #temp table
:setvar tablename "#T" 
:setvar tablescript "CREATE TABLE #T"
*/

USE tempdb 
GO    
CHECKPOINT

DECLARE @LSN NVARCHAR(25)

SELECT @LSN = MAX([Current LSN])
FROM fn_dblog(null, null) 


EXEC(N'BEGIN TRAN StartBatch
SAVE TRAN StartBatch
COMMIT

$(tablescript)
(
[4CA996AC-C7E1-48B5-B48A-E721E7A435F0] INT PRIMARY KEY DEFAULT 0,
InRowFiller char(7000) DEFAULT ''A'',
OffRowFiller varchar(8000) DEFAULT REPLICATE(''B'',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast(''C'' as varchar(max)),10000)
)


BEGIN TRAN InsertFirstRow
SAVE TRAN InsertFirstRow
COMMIT

INSERT INTO $(tablename)
DEFAULT VALUES

BEGIN TRAN Insert9Rows
SAVE TRAN Insert9Rows
COMMIT


INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP 9 ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM sys.all_columns

BEGIN TRAN InsertFailure
SAVE TRAN InsertFailure
COMMIT


/*Try and Insert 10 rows, the 10th one will cause a constraint violation*/
BEGIN TRY
INSERT INTO $(tablename) ([4CA996AC-C7E1-48B5-B48A-E721E7A435F0])
SELECT TOP (10) (10 + ROW_NUMBER() OVER (ORDER BY (SELECT 0))) % 20
FROM sys.all_columns
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
END CATCH

BEGIN TRAN Update10Rows
SAVE TRAN Update10Rows
COMMIT

UPDATE $(tablename)
SET InRowFiller = LOWER(InRowFiller),
    OffRowFiller  =LOWER(OffRowFiller),
    LOBFiller  =LOWER(LOBFiller)


BEGIN TRAN Delete10Rows
SAVE TRAN Delete10Rows
COMMIT

DELETE FROM  $(tablename)
BEGIN TRAN AfterDelete
SAVE TRAN AfterDelete
COMMIT

BEGIN TRAN EndBatch
SAVE TRAN EndBatch
COMMIT')


DECLARE @LSN_HEX NVARCHAR(25) = 
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 1, 8),2) AS INT) AS VARCHAR) + ':' +
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 10, 8),2) AS INT) AS VARCHAR) + ':' +
        CAST(CAST(CONVERT(varbinary,SUBSTRING(@LSN, 19, 4),2) AS INT) AS VARCHAR)        

SELECT 
    [Operation],
    [Context],
    [AllocUnitName],
    [Transaction Name],
    [Description]
FROM   fn_dblog(@LSN_HEX, null) AS D
WHERE  [Current LSN] > @LSN  

SELECT CASE
         WHEN GROUPING(Operation) = 1 THEN 'Total'
         ELSE Operation
       END AS Operation,
       Context,
       AllocUnitName,
       COALESCE(SUM([Log Record Length]), 0) AS [Size in Bytes],
       COUNT(*)                              AS Cnt
FROM   fn_dblog(@LSN_HEX, null) AS D
WHERE  [Current LSN] > @LSN  
GROUP BY GROUPING SETS((Operation, Context, AllocUnitName),())

Résultats

+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
|                       |                    |                           |             @TV      |             #TV      |                  |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Operation             | Context            | AllocUnitName             | Size in Bytes | Cnt  | Size in Bytes | Cnt  | Difference Bytes |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| LOP_ABORT_XACT        | LCX_NULL           |                           | 52            | 1    | 52            | 1    |                  |
| LOP_BEGIN_XACT        | LCX_NULL           |                           | 6056          | 50   | 6056          | 50   |                  |
| LOP_COMMIT_XACT       | LCX_NULL           |                           | 2548          | 49   | 2548          | 49   |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 624           | 3    | 624           | 3    |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysrowsets.clust      | 208           | 1    | 208           | 1    |                  |
| LOP_COUNT_DELTA       | LCX_CLUSTERED      | sys.sysrscols.clst        | 832           | 4    | 832           | 4    |                  |
| LOP_CREATE_ALLOCCHAIN | LCX_NULL           |                           | 120           | 3    | 120           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 720           | 9    | 720           | 9    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysallocunits.clust   | 444           | 3    | 444           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysallocunits.nc      | 276           | 3    | 276           | 3    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syscolpars.clst       | 628           | 4    | 628           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syscolpars.nc         | 484           | 4    | 484           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysidxstats.clst      | 176           | 1    | 176           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysidxstats.nc        | 144           | 1    | 144           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysiscols.clst        | 100           | 1    | 100           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysiscols.nc1         | 88            | 1    | 88            | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysobjvalues.clst     | 596           | 5    | 596           | 5    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysrowsets.clust      | 132           | 1    | 132           | 1    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysrscols.clst        | 528           | 4    | 528           | 4    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.clst       | 1040          | 6    | 1276          | 6    | 236              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc1        | 820           | 6    | 1060          | 6    | 240              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc2        | 820           | 6    | 1060          | 6    | 240              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.sysschobjs.nc3        | 480           | 6    | 480           | 6    |                  |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syssingleobjrefs.clst | 96            | 1    |               |      | -96              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | sys.syssingleobjrefs.nc1  | 88            | 1    |               |      | -88              |
| LOP_DELETE_ROWS       | LCX_MARK_AS_GHOST  | Unknown Alloc Unit        | 72092         | 19   | 72092         | 19   |                  |
| LOP_DELETE_ROWS       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 16348         | 37   | 16348         | 37   |                  |
| LOP_FORMAT_PAGE       | LCX_HEAP           | Unknown Alloc Unit        | 1596          | 19   | 1596          | 19   |                  |
| LOP_FORMAT_PAGE       | LCX_IAM            | Unknown Alloc Unit        | 252           | 3    | 252           | 3    |                  |
| LOP_FORMAT_PAGE       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 84            | 1    | 84            | 1    |                  |
| LOP_FORMAT_PAGE       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 4788          | 57   | 4788          | 57   |                  |
| LOP_HOBT_DDL          | LCX_NULL           |                           | 108           | 3    | 108           | 3    |                  |
| LOP_HOBT_DELTA        | LCX_NULL           |                           | 9600          | 150  | 9600          | 150  |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 456           | 3    | 456           | 3    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.syscolpars.clst       | 644           | 4    | 644           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysidxstats.clst      | 180           | 1    | 180           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysiscols.clst        | 104           | 1    | 104           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysobjvalues.clst     | 616           | 5    | 616           | 5    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysrowsets.clust      | 136           | 1    | 136           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysrscols.clst        | 544           | 4    | 544           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.sysschobjs.clst       | 1064          | 6    | 1300          | 6    | 236              |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | sys.syssingleobjrefs.clst | 100           | 1    |               |      | -100             |
| LOP_INSERT_ROWS       | LCX_CLUSTERED      | Unknown Alloc Unit        | 135888        | 19   | 135888        | 19   |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_INTERIOR | Unknown Alloc Unit        | 1596          | 19   | 1596          | 19   |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysallocunits.nc      | 288           | 3    | 288           | 3    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.syscolpars.nc         | 500           | 4    | 500           | 4    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysidxstats.nc        | 148           | 1    | 148           | 1    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysiscols.nc1         | 92            | 1    | 92            | 1    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc1        | 844           | 6    | 1084          | 6    | 240              |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc2        | 844           | 6    | 1084          | 6    | 240              |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.sysschobjs.nc3        | 504           | 6    | 504           | 6    |                  |
| LOP_INSERT_ROWS       | LCX_INDEX_LEAF     | sys.syssingleobjrefs.nc1  | 92            | 1    |               |      | -92              |
| LOP_INSERT_ROWS       | LCX_TEXT_MIX       | Unknown Alloc Unit        | 5112          | 71   | 5112          | 71   |                  |
| LOP_MARK_SAVEPOINT    | LCX_NULL           |                           | 508           | 8    | 508           | 8    |                  |
| LOP_MODIFY_COLUMNS    | LCX_CLUSTERED      | Unknown Alloc Unit        | 1560          | 10   | 1560          | 10   |                  |
| LOP_MODIFY_HEADER     | LCX_HEAP           | Unknown Alloc Unit        | 3780          | 45   | 3780          | 45   |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.syscolpars.clst       | 384           | 4    | 384           | 4    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysidxstats.clst      | 100           | 1    | 100           | 1    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysrowsets.clust      | 92            | 1    | 92            | 1    |                  |
| LOP_MODIFY_ROW        | LCX_CLUSTERED      | sys.sysschobjs.clst       | 1144          | 13   | 1144          | 13   |                  |
| LOP_MODIFY_ROW        | LCX_IAM            | Unknown Alloc Unit        | 4224          | 48   | 4224          | 48   |                  |
| LOP_MODIFY_ROW        | LCX_PFS            | Unknown Alloc Unit        | 13632         | 169  | 13632         | 169  |                  |
| LOP_MODIFY_ROW        | LCX_TEXT_MIX       | Unknown Alloc Unit        | 108640        | 120  | 108640        | 120  |                  |
| LOP_ROOT_CHANGE       | LCX_CLUSTERED      | sys.sysallocunits.clust   | 960           | 10   | 960           | 10   |                  |
| LOP_SET_BITS          | LCX_GAM            | Unknown Alloc Unit        | 1200          | 20   | 1200          | 20   |                  |
| LOP_SET_BITS          | LCX_IAM            | Unknown Alloc Unit        | 1080          | 18   | 1080          | 18   |                  |
| LOP_SET_BITS          | LCX_SGAM           | Unknown Alloc Unit        | 120           | 2    | 120           | 2    |                  |
| LOP_SHRINK_NOOP       | LCX_NULL           |                           |               |      | 32            | 1    | 32               |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+
| Total                 |                    |                           | 410144        | 1095 | 411232        | 1092 | 1088             |
+-----------------------+--------------------+---------------------------+---------------+------+---------------+------+------------------+

1
+1 Juste par curiosité (et pour être un peu pédant). La question est / était assez ancienne (août 2008), donc il s'agissait de SQL 2005. Maintenant, nous sommes en 2011 (fin) et le dernier SQL est 2008 R2 plus la version bêta de Denali. Quelle version avez-vous utilisée?
xanatos

2
@xanatos - 2008. En 2005, les variables de table seraient en fait désavantagées car elles INSERT ... SELECTn'étaient pas enregistrées de façon minimale et vous ne pouvez pas utiliser de SELECT INTO ... variable de table.
Martin Smith

1
Merci @MartinSmith, mis à jour ma réponse pour supprimer la réclamation concernant la journalisation.
Rory

18

Dans quels scénarios l'un surpasse-t-il l'autre?

Pour les petites tables (moins de 1 000 lignes), utilisez une variable temporaire, sinon utilisez une table temporaire.


17
Des données à l'appui? Ce n'est pas très utile comme une affirmation en soi.
Michael Myers

8
Microsoft recommande une limite de 100 lignes: msdn.microsoft.com/en-us/library/ms175010.aspx (voir la section Meilleures pratiques).
Artemix

Voir ma réponse ci-dessous pour une explication.
Weihui Guo

17

@wcm - en fait pour choisir la variable de table n'est pas seulement Ram - elle peut être partiellement stockée sur le disque.

Une table temporaire peut avoir des index, tandis qu'une variable de table ne peut avoir qu'un index primaire. Si la vitesse est un problème, les variables de la table peuvent être plus rapides, mais évidemment s'il y a beaucoup d'enregistrements, ou la nécessité de rechercher la table temporaire d'un index clusterisé, alors une table temporaire serait mieux.

Bon article de fond


2
Bon article de fond +1. Je vais supprimer ma réponse car la modifier ne laisserait pas grand-chose et il y a déjà tellement de bonnes réponses
wcm

12
  1. Table temporaire: Une table temporaire est facile à créer et à sauvegarder des données.

    Variable de table: Mais la variable de table implique l'effort lorsque nous créons habituellement les tables normales.

  2. Table temporaire: le résultat de la table temporaire peut être utilisé par plusieurs utilisateurs.

    Variable de table: Mais la variable de table ne peut être utilisée que par l'utilisateur actuel. 

  3. Table temporaire: La table temporaire sera stockée dans la tempdb. Cela fera du trafic réseau. Lorsque nous avons de grandes données dans la table temporaire, elles doivent fonctionner dans la base de données. Un problème de performances existera.

    Variable de table: Mais une variable de table stockera dans la mémoire physique pour certaines des données, puis plus tard lorsque la taille augmentera, elle sera déplacée vers tempdb.

  4. Table temporaire: La table temporaire peut effectuer toutes les opérations DDL. Il permet de créer des index, de les supprimer, de les modifier, etc.,

    Variable de table: alors que la variable de table ne permet pas d'effectuer les opérations DDL. Mais la variable de table nous permet de créer l'index cluster uniquement.

  5. Table temporaire: La table temporaire peut être utilisée pour la session en cours ou globale. Pour qu'une session multi-utilisateurs puisse utiliser les résultats du tableau.

    Variable de table: Mais la variable de table peut être utilisée jusqu'à ce programme. (Procédure stockée)

  6. Table temporaire: la variable temporaire ne peut pas utiliser les transactions. Lorsque nous effectuons les opérations DML avec la table temporaire, il peut être annulé ou validé les transactions.

    Variable de table: Mais nous ne pouvons pas le faire pour la variable de table.

  7. Table temporaire: les fonctions ne peuvent pas utiliser la variable temporaire. De plus, nous ne pouvons pas faire l'opération DML dans les fonctions.

    Variable de table: Mais la fonction nous permet d'utiliser la variable de table. Mais en utilisant la variable de table, nous pouvons le faire.

  8. Table temporaire: La procédure stockée effectuera la recompilation (ne peut pas utiliser le même plan d'exécution) lorsque nous utilisons la variable temporaire pour chaque appel de sous-séquence.

    Variable de table: alors que la variable de table ne fera pas comme ça.


8

Pour tous ceux qui croient au mythe que les variables temporaires ne sont qu'en mémoire

Premièrement, la variable de table N'EST PAS nécessairement résidente en mémoire. Sous la pression de la mémoire, les pages appartenant à une variable de table peuvent être déplacées vers tempdb.

Lisez l'article ici: Variable TempDB :: Table vs table temporaire locale


3
Pouvez-vous modifier vos réponses en une seule réponse portant sur les deux points?
Joshua Drake

7

L'autre différence principale est que les variables de table n'ont pas de statistiques de colonne, contrairement aux tables temporaires. Cela signifie que l'optimiseur de requêtes ne sait pas combien de lignes se trouvent dans la variable de table (il en suppose 1), ce qui peut conduire à des plans hautement non optimaux générés si la variable de table a en fait un grand nombre de lignes.


2
La rowscolonne dans sys.partitionsest conservée pour les variables de table afin qu'elle sache réellement combien de lignes se trouvent dans la table. Cela peut être vu en utilisant OPTION (RECOMPILE). Mais le manque de statistiques de colonne signifie qu'il ne peut pas estimer les prédicats de colonne spécifiques.
Martin Smith

7

Citation tirée de; Internes et dépannage professionnels de SQL Server 2012

Statistiques La principale différence entre les tables temporaires et les variables de table est que les statistiques ne sont pas créées sur les variables de table. Cela a deux conséquences majeures, la première étant que l'Optimiseur de requête utilise une estimation fi xe du nombre de lignes dans une variable de table, quelles que soient les données qu'elle contient. De plus, l'ajout ou la suppression de données ne modifie pas l'estimation.

Index Vous ne pouvez pas créer d'index sur des variables de table bien que vous puissiez créer des contraintes. Cela signifie qu'en créant des clés primaires ou des contraintes uniques, vous pouvez avoir des index (car ils sont créés pour prendre en charge les contraintes) sur les variables de table. Même si vous avez des contraintes, et donc des index qui auront des statistiques, les index ne seront pas utilisés lors de la compilation de la requête car ils n'existeront pas au moment de la compilation et ne provoqueront pas de recompilations.

Modifications de schéma Les modifications de schéma sont possibles sur des tables temporaires mais pas sur des variables de table. Bien que des modifications de schéma soient possibles sur des tables temporaires, évitez de les utiliser car elles provoquent des recompilations d'instructions qui utilisent les tables.

Tableaux temporaires et variables de tableau

LES VARIABLES DES TABLEAUX NE SONT PAS CRÉÉES EN MÉMOIRE

Il existe une idée fausse commune selon laquelle les variables de table sont des structures en mémoire et, en tant que telles, fonctionneront plus rapidement que les tables temporaires . Merci à un DMV appelé sys. dm _ db _ session _ space _ usage, qui montre l'utilisation de tempdb par session, vous pouvez prouver que ce n'est pas le cas . Après avoir redémarré SQL Server pour effacer la DMV, exécutez le script suivant pour confirmer que votre identifiant de session _ renvoie 0 pour l'utilisateur _ objets _ alloc _ page _ nombre:

SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

Vous pouvez maintenant vérifier l'espace utilisé par une table temporaire en exécutant le script suivant pour créer une table temporaire avec une colonne et la remplir avec une ligne:

CREATE TABLE #TempTable ( ID INT ) ;
INSERT INTO #TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

Les résultats sur mon serveur indiquent que la table a été allouée une page dans tempdb. Exécutez maintenant le même script mais utilisez cette fois une variable de table:

DECLARE @TempTable TABLE ( ID INT ) ;
INSERT INTO @TempTable ( ID )
VALUES ( 1 ) ;
GO
SELECT session_id,
database_id,
user_objects_alloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id > 50 ;

Lequel utiliser?

Que vous utilisiez ou non des tables temporaires ou des variables de table doit être décidé par des tests approfondis, mais il est préférable de se pencher vers les tables temporaires par défaut car il y a beaucoup moins de choses qui peuvent mal tourner .

J'ai vu des clients développer du code à l'aide de variables de table car ils traitaient une petite quantité de lignes, et c'était plus rapide qu'une table temporaire, mais quelques années plus tard, il y avait des centaines de milliers de lignes dans la variable de table et les performances étaient terribles , alors essayez de prévoir une certaine planification de la capacité lorsque vous prenez votre décision!


En fait, des statistiques sont créées sur les variables de table, voir stackoverflow.com/questions/42824366/…
YuFeng Shen

4

Une autre différence:

Une table var n'est accessible qu'à partir des instructions de la procédure qui la crée, pas à partir d'autres procédures appelées par cette procédure ou du SQL dynamique imbriqué (via exec ou sp_executesql).

La portée d'une table temporaire, en revanche, inclut du code dans des procédures appelées et du SQL dynamique imbriqué.

Si la table créée par votre procédure doit être accessible à partir d'autres procédures appelées ou SQL dynamique, vous devez utiliser une table temporaire. Cela peut être très pratique dans des situations complexes.


2

Les différences entre Temporary Tables (##temp/#temp)et Table Variables (@table)sont les suivantes:

  1. Table variable (@table)est créé dans le memory. Alors que, un Temporary table (##temp/#temp)est créé dans le tempdb database. Cependant, s'il y a une pression mémoire, les pages appartenant à une variable de table peuvent être poussées vers tempdb.

  2. Table variablesne peut pas être impliqué dans transactions, logging or locking. Cela fait @table faster then #temp. La table variable est donc plus rapide que la table temporaire.

  3. Temporary tablepermet des modifications de schéma contrairement Table variables.

  4. Temporary tablessont visibles dans la routine créée et également dans les routines enfants. Alors que les variables de table ne sont visibles que dans la routine créée.

  5. Temporary tablessont autorisés CREATE INDEXesalors que, Table variablesne sont pas autorisés à la CREATE INDEXplace, ils peuvent avoir un index en utilisant Primary Key or Unique Constraint.


1

Considérez également que vous pouvez souvent remplacer les deux par des tables dérivées qui peuvent également être plus rapides. Comme pour tout réglage des performances, cependant, seuls les tests réels par rapport à vos données réelles peuvent vous indiquer la meilleure approche pour votre requête particulière.


1

Cela me surprend que personne n'ait mentionné la principale différence entre ces deux éléments: la table temporaire prend en charge l' insertion parallèle, contrairement à la variable de table. Vous devriez pouvoir voir la différence par rapport au plan d'exécution. Et voici la vidéo des ateliers SQL sur le canal 9 .

Cela explique également pourquoi vous devez utiliser une variable temporaire pour les petites tables, sinon utilisez une table temporaire, comme SQLMenace l'a déjà répondu .

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.