Cela semble être un domaine avec pas mal de mythes et d'opinions contradictoires.
Alors, quelle est la différence entre une variable de table et une table temporaire locale dans SQL Server?
Cela semble être un domaine avec pas mal de mythes et d'opinions contradictoires.
Alors, quelle est la différence entre une variable de table et une table temporaire locale dans SQL Server?
Réponses:
Contenu
Caveat
Cette réponse traite des variables de table "classiques" introduites dans SQL Server 2000. SQL Server 2014 en mémoire OLTP introduit les types de table optimisés en mémoire. Les instances de variables de table de celles-ci diffèrent à bien des égards de celles discutées ci-dessous! ( plus de détails ).
Emplacement de stockage
Aucune différence. Les deux sont stockés dans tempdb
.
Je l'ai vu suggérer que ce n'est pas toujours le cas pour les variables de table, mais ceci peut être vérifié à partir des éléments ci-dessous.
DECLARE @T TABLE(X INT)
INSERT INTO @T VALUES(1),(2)
SELECT sys.fn_PhysLocFormatter(%%physloc%%) AS [File:Page:Slot]
FROM @T
Exemple de résultats (les emplacements montrant dans tempdb
les 2 lignes sont stockés)
File:Page:Slot
----------------
(1:148:0)
(1:148:1)
Emplacement logique
@table_variables
se comporter davantage comme s'ils faisaient partie de la base de données actuelle par rapport aux #temp
tables. Pour les variables de table (depuis 2005), les classements de colonnes, s'ils ne sont pas spécifiés explicitement, seront ceux de la base de données actuelle, tandis que pour les #temp
tableaux, ils utiliseront le classement par défaut de tempdb
( Plus de détails ). De plus, les types de données définis par l'utilisateur et les collections XML doivent être dans tempdb pour pouvoir être utilisés dans les #temp
tables, mais les variables de table peuvent les utiliser à partir de la base de données actuelle ( Source ).
SQL Server 2012 introduit les bases de données contenues. le comportement des tables temporaires dans ces diffère (h / t Aaron)
Dans une base de données contenue, les données d'une table temporaire sont regroupées dans le classement de la base de données contenue.
- Toutes les métadonnées associées aux tables temporaires (par exemple, noms de table et de colonne, index, etc.) figureront dans le classement du catalogue.
- Les contraintes nommées ne peuvent pas être utilisées dans les tables temporaires.
- Les tables temporaires ne peuvent pas faire référence à des types définis par l'utilisateur, à des collections de schémas XML ou à des fonctions définies par l'utilisateur.
Visibilité à différentes portées
@table_variables
ne sont accessibles que dans le lot et la portée dans lesquels ils sont déclarés. #temp_tables
sont accessibles au sein de lots enfants (déclencheurs imbriqués, procédure, exec
appels). #temp_tables
créé à l’extérieur de la portée ( @@NESTLEVEL=0
) peut également s’étendre sur plusieurs lots, jusqu’à la fin de la session. Aucun de ces types d'objet ne peut être créé dans un lot enfant et être accédé dans la portée de l'appelant, comme indiqué ultérieurement (les ##temp
tables globales peuvent l' être).
Durée de vie
@table_variables
sont créées implicitement lorsqu'un lot contenant une DECLARE @.. TABLE
instruction est exécutée (avant que tout code utilisateur de ce lot ne soit exécuté) et sont supprimées implicitement à la fin.
Bien que l'analyseur ne vous permette pas d'essayer d'utiliser la variable de table avant l' DECLARE
instruction, la création implicite est visible ci-dessous.
IF (1 = 0)
BEGIN
DECLARE @T TABLE(X INT)
END
--Works fine
SELECT *
FROM @T
#temp_tables
sont créés explicitement lorsque l' CREATE TABLE
instruction TSQL est rencontrée et peuvent être supprimés explicitement avec DROP TABLE
ou seront implicitement supprimés lorsque le lot se termine (si créé dans un lot enfant @@NESTLEVEL > 0
) ou lorsque la session se termine autrement.
NB: Dans les routines stockées, les deux types d'objet peuvent être mis en cache plutôt que de créer et de supprimer à plusieurs reprises de nouvelles tables. Il y a des restrictions sur le moment où cette mise en cache peut se produire, mais il est possible de violer #temp_tables
mais pour lesquelles les restrictions sur @table_variables
empêcher de toute façon. La surcharge de maintenance pour les #temp
tables en cache est légèrement supérieure à celle des variables de table, comme illustré ici .
Métadonnées d'objet
C'est essentiellement la même chose pour les deux types d'objet. Il est stocké dans les tables de base du système dans tempdb
. Il est plus simple de voir pour une #temp
table, cependant, comme cela OBJECT_ID('tempdb..#T')
peut être utilisé pour entrer dans les tables système et que le nom généré en interne est plus étroitement corrélé avec le nom défini dans l' CREATE TABLE
instruction. Pour les variables de table, la object_id
fonction ne fonctionne pas et le nom interne est entièrement généré par le système, sans relation avec le nom de la variable. La figure ci-dessous montre que les métadonnées sont toujours là, en tapant un nom de colonne (espérons-le unique). Pour les tables sans nom de colonne unique, object_id peut être déterminé à l'aide DBCC PAGE
tant qu'elles ne sont pas vides.
/*Declare a table variable with some unusual options.*/
DECLARE @T TABLE
(
[dba.se] INT IDENTITY PRIMARY KEY NONCLUSTERED,
A INT CHECK (A > 0),
B INT DEFAULT 1,
InRowFiller char(1000) DEFAULT REPLICATE('A',1000),
OffRowFiller varchar(8000) DEFAULT REPLICATE('B',8000),
LOBFiller varchar(max) DEFAULT REPLICATE(cast('C' as varchar(max)),10000),
UNIQUE CLUSTERED (A,B)
WITH (FILLFACTOR = 80,
IGNORE_DUP_KEY = ON,
DATA_COMPRESSION = PAGE,
ALLOW_ROW_LOCKS=ON,
ALLOW_PAGE_LOCKS=ON)
)
INSERT INTO @T (A)
VALUES (1),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13)
SELECT t.object_id,
t.name,
p.rows,
a.type_desc,
a.total_pages,
a.used_pages,
a.data_pages,
p.data_compression_desc
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.tables AS t
ON t.object_id = p.object_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se'
Sortie
Duplicate key was ignored.
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| object_id | name | rows | type_desc | total_pages | used_pages | data_pages | data_compression_desc |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | PAGE |
| 574625090 | #22401542 | 13 | LOB_DATA | 24 | 19 | 0 | PAGE |
| 574625090 | #22401542 | 13 | ROW_OVERFLOW_DATA | 16 | 14 | 0 | PAGE |
| 574625090 | #22401542 | 13 | IN_ROW_DATA | 2 | 2 | 1 | NONE |
+-----------+-----------+------+-------------------+-------------+------------+------------+-----------------------+
Transactions
Les opérations sur @table_variables
sont effectuées en tant que transactions système, indépendamment de toute transaction utilisateur externe, alors que les #temp
opérations de table équivalentes seraient effectuées dans le cadre de la transaction utilisateur elle-même. Pour cette raison, une ROLLBACK
commande affectera une #temp
table mais laissera celle-ci @table_variable
intacte.
DECLARE @T TABLE(X INT)
CREATE TABLE #T(X INT)
BEGIN TRAN
INSERT #T
OUTPUT INSERTED.X INTO @T
VALUES(1),(2),(3)
/*Both have 3 rows*/
SELECT * FROM #T
SELECT * FROM @T
ROLLBACK
/*Only table variable now has rows*/
SELECT * FROM #T
SELECT * FROM @T
DROP TABLE #T
Enregistrement
Tous deux génèrent des enregistrements de journal dans le tempdb
journal des transactions. Une idée fausse commune est que ce n'est pas le cas pour les variables de table, donc un script démontrant cela est ci-dessous, il déclare une variable de table, ajoute quelques lignes, puis les met à jour et les supprime.
Étant donné que la variable de table est créée et supprimée implicitement au début et à la fin du lot, il est nécessaire d’utiliser plusieurs lots afin de voir la journalisation complète.
USE tempdb;
/*
Don't run this on a busy server.
Ideally should be no concurrent activity at all
*/
CHECKPOINT;
GO
/*
The 2nd column is binary to allow easier correlation with log output shown later*/
DECLARE @T TABLE ([C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3] INT, B BINARY(10))
INSERT INTO @T
VALUES (1, 0x41414141414141414141),
(2, 0x41414141414141414141)
UPDATE @T
SET B = 0x42424242424242424242
DELETE FROM @T
/*Put allocation_unit_id into CONTEXT_INFO to access in next batch*/
DECLARE @allocId BIGINT, @Context_Info VARBINARY(128)
SELECT @Context_Info = allocation_unit_id,
@allocId = a.allocation_unit_id
FROM sys.system_internals_allocation_units a
INNER JOIN sys.partitions p
ON p.hobt_id = a.container_id
INNER JOIN sys.columns c
ON c.object_id = p.object_id
WHERE ( c.name = 'C71ACF0B-47E9-4CAD-9A1E-0C687A8F9CF3' )
SET CONTEXT_INFO @Context_Info
/*Check log for records related to modifications of table variable itself*/
SELECT Operation,
Context,
AllocUnitName,
[RowLog Contents 0],
[Log Record Length]
FROM fn_dblog(NULL, NULL)
WHERE AllocUnitId = @allocId
GO
/*Check total log usage including updates against system tables*/
DECLARE @allocId BIGINT = CAST(CONTEXT_INFO() AS BINARY(8));
WITH T
AS (SELECT Operation,
Context,
CASE
WHEN AllocUnitId = @allocId THEN 'Table Variable'
WHEN AllocUnitName LIKE 'sys.%' THEN 'System Base Table'
ELSE AllocUnitName
END AS AllocUnitName,
[Log Record Length]
FROM fn_dblog(NULL, NULL) AS D)
SELECT Operation = CASE
WHEN GROUPING(Operation) = 1 THEN 'Total'
ELSE Operation
END,
Context,
AllocUnitName,
[Size in Bytes] = COALESCE(SUM([Log Record Length]), 0),
Cnt = COUNT(*)
FROM T
GROUP BY GROUPING SETS( ( Operation, Context, AllocUnitName ), ( ) )
ORDER BY GROUPING(Operation),
AllocUnitName
Résultats
Pour autant que je sache, les opérations sur les deux génèrent des quantités de journalisation à peu près égales.
Alors que la quantité de l' exploitation forestière est très similaire une différence importante est que les enregistrements de journaux liés aux #temp
tableaux ne peuvent pas être effacés jusqu'à ce que tout contenant termine transaction d'utilisateur pour une transaction longue course qui à un moment donné , écrit à des #temp
tables empêche la troncature du journal en tempdb
tandis que les opérations autonomes ne sont pas générés pour les variables de table.
Les variables de table ne prennent pas en charge TRUNCATE
et peuvent donc présenter un inconvénient de journalisation lorsqu'il est nécessaire de supprimer toutes les lignes d'une table (bien que pour de très petites tables, DELETE
elles fonctionnent mieux de toute façon )
Cardinalité
De nombreux plans d'exécution impliquant des variables de table afficheront une seule ligne estimée en tant que sortie. L'inspection des propriétés de la variable de table indique que SQL Server estime que la variable de table ne contient aucune ligne (@Paul White explique ici pourquoi 1 ligne sera émise à partir d'une table de lignes zéro .
Toutefois, les résultats présentés dans la section précédente indiquent un rows
décompte précis en sys.partitions
. Le problème est que, dans la plupart des cas, les instructions faisant référence aux variables de table sont compilées lorsque la table est vide. Si l'instruction est (re) compilée après avoir @table_variable
été renseignée, elle sera utilisée pour la cardinalité de la table (cela peut être dû à un explicite recompile
ou peut-être parce que l'instruction fait également référence à un autre objet entraînant une compilation ou une recompilation différée.)
DECLARE @T TABLE(I INT);
INSERT INTO @T VALUES(1),(2),(3),(4),(5)
CREATE TABLE #T(I INT)
/*Reference to #T means this statement is subject to deferred compile*/
SELECT * FROM @T WHERE NOT EXISTS(SELECT * FROM #T)
DROP TABLE #T
Le plan indique le nombre estimé de lignes exactes après la compilation différée.
Dans SQL Server 2012 SP2, l'indicateur de trace 2453 est introduit. Plus de détails sont sous "Moteur relationnel" ici .
Lorsque cet indicateur de trace est activé, les recompilations automatiques peuvent prendre en compte la cardinalité modifiée, comme indiqué plus en détail ultérieurement.
NB: Sur Azure au niveau de compatibilité 150, la compilation de l'instruction est maintenant différée jusqu'à la première exécution . Cela signifie qu'il ne sera plus soumis au problème d'estimation de zéro ligne.
Aucune statistique de colonne
Avoir une cardinalité de table plus précise ne signifie pas pour autant que le nombre estimé de lignes sera plus précis (à moins d'effectuer une opération sur toutes les lignes de la table). SQL Server ne gère pas du tout les statistiques de colonne pour les variables de table, il faut donc se baser sur des suppositions basées sur le prédicat de comparaison (par exemple, 10% de la table seront renvoyés pour une =
colonne non unique ou 30% pour une >
comparaison). En revanche, les statistiques de colonne sont conservées pour les #temp
tables.
SQL Server conserve le nombre de modifications apportées à chaque colonne. Si le nombre de modifications depuis la compilation du plan dépasse le seuil de recompilation (RT), le plan sera recompilé et les statistiques mises à jour. La RT dépend du type et de la taille de la table.
À partir de la mise en cache des plans dans SQL Server 2008
RT est calculé comme suit. (n fait référence à la cardinalité d'une table lorsqu'un plan de requête est compilé.)
Table permanente
- Si n <= 500, RT = 500.
- Si n> 500, RT = 500 + 0,20 * n.Table temporaire
- Si n <6, RT = 6.
- Si 6 <= n <= 500, RT = 500.
- Si n> 500, RT = 500 + 0,20 * n.
Variable de table
- RT n'existe pas. Par conséquent, les recompilations ne se produisent pas en raison de modifications des cardinalités des variables de table. (Mais voir la note sur la TF 2453 ci-dessous)
l' KEEP PLAN
indicateur peut être utilisé pour définir la RT pour les #temp
tables de la même manière que pour les tables permanentes.
En définitive, les plans d’exécution générés pour les #temp
tables sont de meilleurs ordres de grandeur que @table_variables
lorsque plusieurs lignes sont impliquées, car SQL Server dispose de meilleures informations.
NB1: les variables de table ne comportent pas de statistiques mais peuvent toujours provoquer un événement de recompilation "Statistiques modifiées" sous l'indicateur de suivi 2453 (ne s'applique pas aux plans "triviaux"). Cela semble se produire avec les mêmes seuils de recompilation que ceux indiqués pour les tables temporaires un supplémentaire que si N=0 -> RT = 1
. En d'autres termes, toutes les instructions compilées lorsque la variable de table est vide finissent par obtenir une recompilation et sont corrigées TableCardinality
lors de leur première exécution lorsqu'elles sont non vides. La cardinalité de la table des temps de compilation est stockée dans le plan et si l'instruction est exécutée à nouveau avec la même cardinalité (en raison du flux d'instructions de contrôle ou de la réutilisation d'un plan mis en cache), aucune recompilation n'a lieu.
NB2: Pour les tables temporaires mises en cache dans les procédures stockées, l’histoire de la recompilation est beaucoup plus compliquée que celle décrite ci-dessus. Voir Tables temporaires dans les procédures stockées pour tous les détails sanglants.
Recompiler
Outre les recompilations basées sur les modifications décrites ci #temp
- dessus, les tableaux ci - dessus peuvent également être associés à des compilations supplémentaires simplement parce qu'ils autorisent des opérations interdites pour les variables de tableau qui déclenchent une compilation (par exemple CREATE INDEX
, modifications de DDL , ALTER TABLE
)
Verrouillage
Il a été précisé que les variables de table ne participent pas au verrouillage. Ce n'est pas le cas. En exécutant les sorties ci-dessous dans l'onglet Messages SSMS, vous trouverez les détails des verrous utilisés et validés pour une instruction d'insertion.
DECLARE @tv_target TABLE (c11 int, c22 char(100))
DBCC TRACEON(1200,-1,3604)
INSERT INTO @tv_target (c11, c22)
VALUES (1, REPLICATE('A',100)), (2, REPLICATE('A',100))
DBCC TRACEOFF(1200,-1,3604)
Pour les requêtes SELECT
issues de variables de table, Paul White souligne dans les commentaires que celles-ci sont automatiquement accompagnées d'un NOLOCK
indice implicite . Ceci est montré ci-dessous
DECLARE @T TABLE(X INT);
SELECT X
FROM @T
OPTION (RECOMPILE, QUERYTRACEON 3604, QUERYTRACEON 8607)
*** Output Tree: (trivial plan) ***
PhyOp_TableScan TBL: @T Bmk ( Bmk1000) IsRow: COL: IsBaseRow1002 Hints( NOLOCK )
L'impact de ceci sur le verrouillage pourrait cependant être assez mineur.
SET NOCOUNT ON;
CREATE TABLE #T( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @T TABLE ( [ID] [int] IDENTITY NOT NULL,
[Filler] [char](8000) NULL,
PRIMARY KEY CLUSTERED ([ID] DESC))
DECLARE @I INT = 0
WHILE (@I < 10000)
BEGIN
INSERT INTO #T DEFAULT VALUES
INSERT INTO @T DEFAULT VALUES
SET @I += 1
END
/*Run once so compilation output doesn't appear in lock output*/
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEON(1200,3604,-1)
SELECT *, sys.fn_PhysLocFormatter(%%physloc%%)
FROM @T
PRINT '--*--'
EXEC('SELECT *, sys.fn_PhysLocFormatter(%%physloc%%) FROM #T')
DBCC TRACEOFF(1200,3604,-1)
DROP TABLE #T
Aucune de ces réponses ne donne un ordre de clé d'index indiquant que SQL Server a utilisé une analyse d' allocation ordonnée pour les deux.
J'ai exécuté le script ci-dessus deux fois et les résultats de la deuxième exécution sont présentés ci-dessous.
Process 58 acquiring Sch-S lock on OBJECT: 2:-1325894110:0 (class bit0 ref1) result: OK
--*--
Process 58 acquiring IS lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 acquiring S lock on OBJECT: 2:-1293893996:0 (class bit0 ref1) result: OK
Process 58 releasing lock on OBJECT: 2:-1293893996:0
La sortie de verrouillage pour la variable de table est en effet extrêmement minime, car SQL Server vient d’acquérir un verrou de stabilité du schéma sur l’objet. Mais pour une #temp
table, il est presque aussi léger qu'il supprime un S
verrou au niveau de l'objet . Il est bien sûr possible de spécifier explicitement un NOLOCK
indice ou un READ UNCOMMITTED
niveau d’isolation lors de l’utilisation de #temp
tables.
De manière similaire au problème de la journalisation d'une transaction utilisateur environnante, les verrous sont conservés plus longtemps pour les #temp
tables. Avec le script ci-dessous
--BEGIN TRAN;
CREATE TABLE #T (X INT,Y CHAR(4000) NULL);
INSERT INTO #T (X) VALUES(1)
SELECT CASE resource_type
WHEN 'OBJECT' THEN OBJECT_NAME(resource_associated_entity_id, 2)
WHEN 'ALLOCATION_UNIT' THEN (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.allocation_units a
JOIN tempdb.sys.partitions p ON a.container_id = p.hobt_id
WHERE a.allocation_unit_id = resource_associated_entity_id)
WHEN 'DATABASE' THEN DB_NAME(resource_database_id)
ELSE (SELECT OBJECT_NAME(object_id, 2)
FROM tempdb.sys.partitions
WHERE partition_id = resource_associated_entity_id)
END AS object_name,
*
FROM sys.dm_tran_locks
WHERE request_session_id = @@SPID
DROP TABLE #T
-- ROLLBACK
lorsqu’il est exécuté en dehors d’une transaction utilisateur explicite dans les deux cas, le seul verrou renvoyé lors de la vérification sys.dm_tran_locks
est un verrou partagé sur le fichier DATABASE
.
Lorsque vous annulez la mise en commentaire, les BEGIN TRAN ... ROLLBACK
26 lignes sont renvoyées et indiquent que les verrous sont maintenus à la fois sur l'objet lui-même et sur les lignes de la table système pour permettre l'annulation et empêcher les autres transactions de lire des données non validées. L'opération de variable de table équivalente n'est pas sujette à l'annulation avec la transaction utilisateur et n'a pas besoin de conserver ces verrous pour que nous puissions enregistrer l'instruction suivante, mais les verrous de traçage acquis et libérés dans Profiler ou à l'aide de l'indicateur de trace 1200 indiquent que de nombreux événements de verrouillage se produire.
Les index
Pour les versions antérieures à SQL Server 2014, les index ne peuvent être créés de manière implicite que sur les variables de table en tant qu'effet secondaire de l'ajout d'une contrainte unique ou d'une clé primaire. Cela signifie bien entendu que seuls les index uniques sont pris en charge. Un index non unique non clusterisé sur une table avec un index clusterisé unique peut être simulé. Cependant, il suffit de le déclarer UNIQUE NONCLUSTERED
et d'ajouter la clé CI à la fin de la clé NCI souhaitée (SQL Server le ferait néanmoins en coulisse, même si une valeur non unique NCI pourrait être spécifié)
Comme démontré précédemment, divers index_option
s peuvent être spécifiés dans la déclaration de contrainte, y compris DATA_COMPRESSION
, IGNORE_DUP_KEY
et FILLFACTOR
(bien qu'il ne sert à rien de la définir, cela ne ferait qu'une différence dans la reconstruction d'index et vous ne pouvez pas reconstruire les index sur les variables de table!)
De plus, les variables de table ne prennent pas en charge les INCLUDE
colonnes d, les index filtrés (jusqu'en 2016) ni le partitionnement, alors que les #temp
tables le sont (le schéma de partition doit être créé dans tempdb
).
Index dans SQL Server 2014
Les index non uniques peuvent être déclarés en ligne dans la définition de variable de table dans SQL Server 2014. Un exemple de syntaxe est fourni ci-dessous.
DECLARE @T TABLE (
C1 INT INDEX IX1 CLUSTERED, /*Single column indexes can be declared next to the column*/
C2 INT INDEX IX2 NONCLUSTERED,
INDEX IX3 NONCLUSTERED(C1,C2) /*Example composite index*/
);
Index dans SQL Server 2016
À partir de CTP 3.1, il est maintenant possible de déclarer des index filtrés pour les variables de table. Par RTM , il peut être le cas que les colonnes incluses sont également autorisés mais ils ne seront probablement pas en faire SQL16 en raison des contraintes de ressources
DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)
Parallélisme
Les requêtes qui insèrent dans (ou modifient autrement) @table_variables
ne peuvent pas avoir un plan parallèle, #temp_tables
ne sont pas restreintes de cette manière.
Il semble y avoir une solution de contournement en ce sens que la réécriture suivante permet à la SELECT
partie de se dérouler en parallèle, mais qu’elle finit par utiliser une table temporaire masquée (en arrière-plan).
INSERT INTO @DATA ( ... )
EXEC('SELECT .. FROM ...')
Il n'y a pas de telle limitation dans les requêtes qui sélectionnent des variables de table, comme illustré dans ma réponse ici
Autres différences fonctionnelles
#temp_tables
ne peut pas être utilisé dans une fonction. @table_variables
peut être utilisé à l'intérieur de fichiers UDF scalaires ou à tables d'instructions multiples.@table_variables
ne peut pas avoir de contraintes nommées.@table_variables
ne peut pas être SELECT
-ed INTO
, ALTER
-ed, TRUNCATE
d ou être la cible de DBCC
commandes telles que DBCC CHECKIDENT
ou de SET IDENTITY INSERT
et ne supporte pas les indicateurs de table tels queWITH (FORCESCAN)
CHECK
Les contraintes sur les variables de table ne sont pas prises en compte par l'optimiseur à des fins de simplification, de prédicats implicites ou de détection de contradiction.PAGELATCH_EX
attente. ( Exemple )Mémoire seulement?
Comme indiqué au début, les deux fichiers sont stockés dans tempdb
. Cependant, je n'ai pas précisé s'il y avait une différence de comportement en ce qui concerne l'écriture de ces pages sur disque.
J'ai fait une petite quantité d'essais sur ce sujet maintenant et jusqu'à présent, je n'ai pas vu une telle différence. Dans le test spécifique que j'ai effectué sur mon instance de SQL Server 250, il semble que le point de rupture avant l'écriture du fichier de données soit atteint.
Remarque: le comportement ci-dessous ne se produit plus dans SQL Server 2014 ou SQL Server 2012 SP1 / CU10 ou SP2 / CU1, le graveur avide n'est plus aussi désireux d'écrire des pages sur le disque. Plus de détails sur cette modification dans SQL Server 2014: tempdb Hidden Performance Gem .
Exécuter le script ci-dessous
CREATE TABLE #T(X INT, Filler char(8000) NULL)
INSERT INTO #T(X)
SELECT TOP 250 ROW_NUMBER() OVER (ORDER BY @@SPID)
FROM master..spt_values
DROP TABLE #T
Et la surveillance écrit dans le tempdb
fichier de données avec Process Monitor, je n’en ai vu aucune (sauf de temps en temps sur la page de démarrage de la base de données avec un décalage de 73 728). Après avoir changé 250
en 251
j'ai commencé à voir écrit comme ci-dessous.
La capture d'écran ci-dessus montre 5 * 32 pages écrites et une seule page écrite indiquant que 161 des pages ont été écrites sur le disque. J'ai également obtenu le même seuil de 250 pages lors de tests avec des variables de table. Le script ci-dessous le montre différemment en regardantsys.dm_os_buffer_descriptors
DECLARE @T TABLE (
X INT,
[dba.se] CHAR(8000) NULL)
INSERT INTO @T
(X)
SELECT TOP 251 Row_number() OVER (ORDER BY (SELECT 0))
FROM master..spt_values
SELECT is_modified,
Count(*) AS page_count
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = (SELECT a.allocation_unit_id
FROM tempdb.sys.partitions AS p
INNER JOIN tempdb.sys.system_internals_allocation_units AS a
ON p.hobt_id = a.container_id
INNER JOIN tempdb.sys.columns AS c
ON c.object_id = p.object_id
WHERE c.name = 'dba.se')
GROUP BY is_modified
is_modified page_count
----------- -----------
0 192
1 61
Montrant que 192 pages ont été écrites sur le disque et le drapeau sale effacé. Cela montre également que l'écriture sur le disque ne signifie pas que les pages seront immédiatement expulsées du pool de mémoire tampon. Les requêtes sur cette variable de table peuvent toujours être satisfaites entièrement à partir de la mémoire.
Sur un serveur inactif avec un max server memory
ensemble de pages de pool de mémoire tampon allouées 2000 MB
et DBCC MEMORYSTATUS
indiquant environ 1 843 000 Ko (environ 23 000 pages), j’ai inséré dans les tableaux ci-dessus par lots de 1 000 lignes / pages et pour chaque itération enregistrée.
SELECT Count(*)
FROM sys.dm_os_buffer_descriptors
WHERE database_id = 2
AND allocation_unit_id = @allocId
AND page_type = 'DATA_PAGE'
La variable de table et la #temp
table ont toutes deux donné des graphes presque identiques et ont réussi à maximiser le pool de mémoire tampon avant de parvenir au point où ils n'étaient pas entièrement conservés en mémoire, de sorte qu'il ne semble pas y avoir de limitation particulière de la quantité de mémoire. soit peut consommer.
J'aimerais souligner quelques points basés sur des expériences particulières plutôt que sur des études. En tant que DBA, je suis très nouveau, alors corrigez-moi si nécessaire.