Considérez le MCVE simple suivant:
SET STATISTICS IO, TIME OFF;
USE tempdb;
IF OBJECT_ID(N'tempdb..#t1', N'U') IS NOT NULL DROP TABLE #t1;
CREATE TABLE #t1
(
r int NOT NULL
);
IF OBJECT_ID(N'tempdb..##t1', N'U') IS NOT NULL DROP TABLE ##t1;
CREATE TABLE ##t1
(
r int NOT NULL
);
IF OBJECT_ID(N'dbo.s1', N'U') IS NOT NULL DROP TABLE dbo.s1;
CREATE TABLE dbo.s1
(
r int NOT NULL
PRIMARY KEY CLUSTERED
);
INSERT INTO dbo.s1 (r)
SELECT TOP(10000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM sys.syscolumns sc1
CROSS JOIN sys.syscolumns sc2;
GO
Lorsque j'exécute les insertions suivantes, l'insertion dans #t1
n'affiche pas d'E/S de statistiques pour la table temporaire. Cependant, l'insertion dans ##t1
fait affiche les E/S de statistiques pour la table temporaire.
SET STATISTICS IO, TIME ON;
GO
INSERT INTO #t1 (r)
SELECT r
FROM dbo.s1;
La sortie des statistiques:
Temps d'analyse et de compilation SQL Server: Temps CPU = 0 ms, temps écoulé = 1 ms. Table 's1'. Nombre de scans 1, lectures logiques 19, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0. SQL Server Execution Times: Temps CPU = 16 ms, temps écoulé = 9 ms. (10000 lignes affectées)
INSERT INTO ##t1 (r)
SELECT r
FROM dbo.s1;
Temps d'analyse et de compilation SQL Server: Temps CPU = 0 ms, temps écoulé = 1 ms. Tableau '## t1'. Nombre de balayages 0, lectures logiques 10016, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0. Tableau 's1'. Nombre de scans 1, lectures logiques 19, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0. SQL Server Execution Times: Temps CPU = 47 ms, temps écoulé = 45 ms. (10000 lignes affectées)
Pourquoi y a-t-il autant de lectures sur la table ## temp alors que je ne fais que l'insérer?
La journalisation minimale n'est pas utilisée lors de l'utilisation de INSERT INTO
Et des tables temporaires globales
Insertion d'un million de lignes dans une table temporaire globale en utilisant INSERT INTO
INSERT INTO ##t1 (r)
SELECT top(1000000) s1.r
FROM dbo.s1
CROSS APPLY dbo.s1 S2;
Lors de l'exécution de SELECT * FROM fn_dblog(NULL, NULL)
pendant l'exécution de la requête ci-dessus, ~ 1 million de lignes sont renvoyées.
Une opération LOP_INSERT_ROW
Pour chaque ligne + autres données de journal.
Le même insert sur une table temporaire locale
INSERT INTO #t1 (r)
SELECT top(1000000) s1.r
FROM dbo.s1
CROSS APPLY dbo.s1 S2;
Jusqu'à 700 lignes retournées par SELECT * FROM fn_dblog(NULL, NULL)
Journalisation minimale
Insertion d'un million de lignes dans une table temporaire globale en utilisant SELECT INTO
SELECT top(1000000) s1.r
INTO ##t2
FROM dbo.s1
CROSS APPLY dbo.s1 S2;
SELECT INTO
Une table temporaire globale avec 10k enregistrements
SELECT s1.r
INTO ##t2
FROM dbo.s1;
Heure et IO Statistiques
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
Table 's1'. Scan count 1, logical reads 19, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 16 ms, elapsed time = 10 ms.
SQL Server parse and compile time:
CPU time = 0 ms, elapsed time = 0 ms.
Basé sur cet article de blog nous pouvons ajouter TABLOCK
pour initier une journalisation minimale sur une table de tas
INSERT INTO ##t1 WITH(TABLOCK) (r)
SELECT s1.r
FROM dbo.s1
Lectures logiques faibles
Table 's1'. Scan count 1, logical reads 19, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
(10000 rows affected)
Une partie de ne réponse par @PaulWhite sur la façon d'obtenir une journalisation minimale sur les tables temporaires
Non. Les tables temporaires locales (#temp) sont privées pour la session de création, donc un conseil de verrouillage de table n'est pas requis. Une indication de verrouillage de table serait requise pour une table temporaire globale (## temp) ou une table régulière (dbo.temp) créée dans tempdb, car elles sont accessibles à partir de plusieurs sessions.
Création d'une table régulière pour tester cela:
CREATE TABLE dbo.bla
(
r int NOT NULL
);
Remplir avec 1M d'enregistrements
INSERT INTO bla
SELECT top(1000000)s1.r
FROM dbo.s1
CROSS APPLY dbo.s1 S2;
> 1 M de lectures logiques sur ce tableau
Table 's1'. Scan count 17, logical reads 155, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'bla'. Scan count 0, logical reads 1001607, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Paul White réponse expliquant les lectures logiques reportées sur la table de temp globale
Généralement, les lectures logiques sont signalées pour la table cible lorsque l'insertion n'est pas journalisée de façon minimale.
Ces lectures logiques sont associées à la recherche d'une place dans la structure existante pour ajouter les nouvelles lignes. Les insertions à journalisation minimale utilisent le mécanisme de chargement en bloc, qui alloue de nouvelles pages/extensions entières (et n'a donc pas besoin de lire la structure cible de la même manière).
Conclusion
La conclusion étant que INSERT INTO
N'est pas en mesure d'utiliser une journalisation minimale, ce qui entraîne la journalisation de chaque ligne insérée individuellement dans le fichier journal de tempdb lorsqu'il est utilisé en combinaison avec une table temporaire globale/table normale. Tandis que la table temporaire locale/SELECT INTO
/INSERT INTO ... WITH(TABLOCK)
est capable d'utiliser une journalisation minimale.