Considere o seguinte MCVE simples:
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
Quando executo as seguintes inserções, inserir em #t1
não mostra nenhuma E/S de estatísticas para a tabela temporária. No entanto, inserir em ##t1
mostra E/S de estatísticas para a tabela temporária.
SET STATISTICS IO, TIME ON;
GO
INSERT INTO #t1 (r)
SELECT r
FROM dbo.s1;
A saída das estatísticas:
SQL Server analisa e compila o tempo: Tempo de CPU = 0 ms, tempo decorrido = 1 ms. Tabela 's1'. Contagem de varredura 1, leituras lógicas 19, leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob 0. Tempos de execução do SQL Server: Tempo de CPU = 16 ms, tempo decorrido = 9 ms. (10.000 linhas afetadas)
INSERT INTO ##t1 (r)
SELECT r
FROM dbo.s1;
SQL Server analisa e compila o tempo: Tempo de CPU = 0 ms, tempo decorrido = 1 ms. Tabela '##t1'. Contagem de varredura 0, leituras lógicas 10016, leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob 0. Tabela 's1'. Contagem de varredura 1, leituras lógicas 19, leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob 0. Tempos de execução do SQL Server: Tempo de CPU = 47 ms, tempo decorrido = 45 ms. (10.000 linhas afetadas)
Por que há tantas leituras na tabela ##temp quando estou apenas inserindo nela?
O registro mínimo não está sendo usado ao usar
INSERT INTO
e tabelas temporárias globaisInserindo um milhão de linhas em uma tabela temporária global usando
INSERT INTO
Ao executar
SELECT * FROM fn_dblog(NULL, NULL)
enquanto a consulta acima está em execução, aproximadamente 1 milhão de linhas são retornadas.Uma
LOP_INSERT_ROW
operação para cada linha + outros dados de log.A mesma inserção em uma tabela temporária local
Apenas indo até 700 linhas retornadas por
SELECT * FROM fn_dblog(NULL, NULL)
Registro mínimo
Inserindo um milhão de linhas em uma tabela temporária global usando
SELECT INTO
SELECT INTO
uma tabela temporária global com 10k registrosEstatísticas de tempo e IO
Com base nesta postagem do blog , podemos adicionar
TABLOCK
para iniciar o registro mínimo em uma tabela de heapBaixas leituras lógicas
Parte de uma resposta de @PaulWhite sobre como obter o mínimo de log em tabelas temporárias
Criando uma tabela regular para testar isso:
Preenchendo com 1 milhão de registros
>1M de leituras lógicas nesta tabela
A resposta de Paul White explicando as leituras lógicas relatadas na tabela temporária global
Conclusão
A conclusão é que o
INSERT INTO
não é capaz de usar log mínimo, resultando no log de cada linha inserida individualmente no arquivo de log do tempdb quando usado em combinação com uma tabela temporária global/tabela normal. Considerando que a tabela temporária local/SELECT INTO
/INSERT INTO ... WITH(TABLOCK)
é capaz de usar log mínimo.