lecturas lógicas en la tabla temporal global, pero no en la tabla temporal a nivel de sesión

11

Considere el siguiente MCVE simple:

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

Cuando ejecuto las siguientes inserciones, la inserción en #t1no muestra estadísticas de E / S para la tabla temporal. Sin embargo, la inserción en ##t1 muestra estadísticas de E / S para la tabla temporal.

SET STATISTICS IO, TIME ON;
GO

INSERT INTO #t1 (r)
SELECT r
FROM dbo.s1;

La salida de estadísticas:

Tiempo de análisis y compilación de SQL Server: 
   Tiempo de CPU = 0 ms, tiempo transcurrido = 1 ms.
Tabla 's1'. Cuenta de escaneo 1, lecturas lógicas 19, lecturas físicas 0, lecturas de lectura anticipada 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas lob de lectura anticipada 0.

 Tiempos de ejecución de SQL Server:
   Tiempo de CPU = 16 ms, tiempo transcurrido = 9 ms.

(10000 filas afectadas)
INSERT INTO ##t1 (r)
SELECT r
FROM dbo.s1;
Tiempo de análisis y compilación de SQL Server: 
   Tiempo de CPU = 0 ms, tiempo transcurrido = 1 ms.
Tabla '## t1'. Cuenta de escaneo 0, lecturas lógicas 10016, lecturas físicas 0, lecturas de lectura anticipada 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas de lectura lob 0.
Tabla 's1'. Cuenta de escaneo 1, lecturas lógicas 19, lecturas físicas 0, lecturas de lectura anticipada 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas lob de lectura anticipada 0.

 Tiempos de ejecución de SQL Server:
   Tiempo de CPU = 47 ms, tiempo transcurrido = 45 ms.

(10000 filas afectadas)

¿Por qué hay tantas lecturas en la tabla temporal ## cuando solo la estoy insertando?

Max Vernon
fuente

Respuestas:

11

El registro mínimo no se utiliza al usar INSERT INTOtablas globales y temporales

Insertar un millón de filas en una tabla temporal global utilizando INSERT INTO

INSERT INTO ##t1 (r)
SELECT top(1000000) s1.r
FROM dbo.s1
CROSS APPLY  dbo.s1 S2;

Cuando se ejecuta SELECT * FROM fn_dblog(NULL, NULL)mientras se ejecuta la consulta anterior, se devuelven ~ 1M filas.

ingrese la descripción de la imagen aquí

Una LOP_INSERT_ROWoperación para cada fila + otros datos de registro.


El mismo inserto en una tabla temporal local

INSERT INTO #t1 (r)
SELECT top(1000000) s1.r
FROM dbo.s1
CROSS APPLY  dbo.s1 S2;

Solo subiendo hasta 700 filas devueltas por SELECT * FROM fn_dblog(NULL, NULL)

ingrese la descripción de la imagen aquí

Registro mínimo


Insertar un millón de filas en una tabla temporal global utilizando SELECT INTO

SELECT top(1000000) s1.r
INTO ##t2
FROM dbo.s1
CROSS APPLY  dbo.s1 S2;

ingrese la descripción de la imagen aquí

SELECT INTO una tabla temporal global con 10k registros

SELECT s1.r
INTO ##t2
FROM dbo.s1;

Estadísticas de tiempo e IO

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.

Según esta publicación de blog , podemos agregar TABLOCKpara iniciar un registro mínimo en una tabla de montón

INSERT INTO ##t1 WITH(TABLOCK) (r)
SELECT   s1.r
FROM dbo.s1

Lecturas lógicas bajas

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)

Parte de una respuesta de @PaulWhite sobre cómo lograr un registro mínimo en tablas temporales

No. Las tablas temporales locales (#temp) son privadas para la sesión de creación, por lo que no se requiere una sugerencia de bloqueo de tabla. Se requeriría una sugerencia de bloqueo de tabla para una tabla temporal global (## temp) o una tabla normal (dbo.temp) creada en tempdb, porque se puede acceder a ellas desde múltiples sesiones.

Crear una tabla regular para probar esto:

CREATE TABLE dbo.bla
(
    r int NOT NULL 
);

Llenándolo con registros de 1M

INSERT INTO bla 
SELECT   top(1000000)s1.r
FROM dbo.s1
CROSS APPLY  dbo.s1 S2;

> 1M de lecturas lógicas en esta tabla

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.

La respuesta de Paul White explicando las lecturas lógicas reportadas en la tabla temporal global

En general, las lecturas lógicas se informan para la tabla de destino cuando la inserción no se registra mínimamente.

Estas lecturas lógicas están asociadas con la búsqueda de un lugar en la estructura existente para agregar las nuevas filas. Las inserciones con registro mínimo utilizan el mecanismo de carga masiva, que asigna páginas / extensiones completamente nuevas (y, por lo tanto, no necesita leer la estructura de destino de la misma manera).


Conclusión

La conclusión es que INSERT INTOno puede usar un registro mínimo, lo que resulta en el registro de cada fila insertada individualmente en el archivo de registro de tempdb cuando se usa en combinación con una tabla temporal global / tabla normal. Mientras que la tabla temporal local / SELECT INTO/ INSERT INTO ... WITH(TABLOCK)puede usar un registro mínimo.

Randi Vertongen
fuente