Progreso de la instrucción SELECT INTO

14

Nuestro flujo ETL tiene una instrucción SELECT INTO de larga duración, que crea una tabla sobre la marcha y la completa con varios cientos de millones de registros.

La declaración se parece a algo como SELECT ... INTO DestTable FROM SrcTable

Para fines de monitoreo, nos gustaría tener una idea aproximada del progreso de esta declaración, mientras se ejecuta (recuento de filas, número de bytes escritos o similar).

Intentamos lo siguiente en vano:

-- Is blocked by the SELECT INTO statement:
select count(*) from DestTable with (nolock)

-- Returns 0, 0:
select rows, rowmodctr
from sysindexes with (nolock)
where id = object_id('DestTable')

-- Returns 0:
select rows
from sys.partitions
where object_id = object_id('DestTable')

Además, podemos ver la transacción en sys.dm_tran_active_transactions, pero no pude encontrar una manera de obtener el recuento de filas afectadas en un determinado transaction_id(algo similar a @@ROWCOUNTquizás, pero con el transaction_idargumento como).

Entiendo que en SQL Server, la instrucción SELECT INTO es tanto una instrucción DDL como una DML en una, y como tal, la creación implícita de la tabla será una operación de bloqueo. Todavía creo que debe haber alguna forma inteligente de obtener algún tipo de información de progreso mientras se ejecuta la declaración.

Dan
fuente
Si utilizó una tabla temporal global ## TABLE, ¿podría realizar una selección con recuento en la columna de índice en la ## TABLE para obtener el número de registros ya escritos y aproximar la cantidad total de registros que se escribirán?
CoveGeek

Respuestas:

6

Sospecho que rowsen sys.partitions0 es debido a que aún no se ha comprometido. Pero esto no significa que SQL Server desconozca qué irá allí si la transacción se confirma. La clave está en recordar que todas las operaciones pasan primero por el Buffer Pool (es decir, la memoria), independientemente de COMMIT o ROLLBACK de la operación. Por lo tanto, podemos buscar sys.dm_os_buffer_descriptorsesa información:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

SELECT  --OBJECT_NAME(sp.[object_id]) AS [TableName], sdobd.*, '---', sp.*, '---', sau.*
       SUM(sdobd.[row_count]) AS [BufferPoolRows],
       SUM(sp.[rows]) AS [AllocatedRows],
       COUNT(*) AS [DataPages]
FROM sys.dm_os_buffer_descriptors sdobd
INNER JOIN  sys.allocation_units sau
        ON sau.[allocation_unit_id] = sdobd.[allocation_unit_id]
INNER JOIN  sys.partitions sp
        ON  (   sau.[type] = 1
            AND sau.[container_id] = sp.[partition_id]) -- IN_ROW_DATA
        OR  (   sau.[type] = 2
            AND sau.[container_id] = sp.[hobt_id]) -- LOB_DATA
        OR  (   sau.[type] = 3
            AND sau.[container_id] = sp.[partition_id]) -- ROW_OVERFLOW_DATA
WHERE   sdobd.[database_id] = DB_ID()
AND     sdobd.[page_type] = N'DATA_PAGE'
AND     sp.[object_id] = (SELECT so.[object_id]
                          FROM   sys.objects so
                          WHERE  so.[name] = 'TestDump')

Si desea ver los detalles, descomente la primera fila de elementos en la SELECTlista, comente las 3 líneas restantes.

Probé ejecutando lo siguiente en una sesión y luego ejecutando repetidamente la consulta anterior en otra.

SELECT so1.*
INTO   dbo.TestDump
FROM   sys.objects so1
CROSS JOIN sys.objects so2
CROSS JOIN sys.objects so3;
Solomon Rutzky
fuente
1
Esto es creativo Solo quiero agregar una advertencia de que enumerar una gran agrupación de almacenamiento intermedio es muy lento.
Usr
1
Esto supone que todavía no se han desalojado páginas del grupo de búferes.
Martin Smith
@MartinSmith ¿Se pueden desalojar las páginas antes de la confirmación?
Solomon Rutzky
55
@srutzky: sí. El registro de transacciones tiene toda la información necesaria para deshacer. Las páginas sucias se pueden escribir en el disco, por ejemplo, en un punto de control o por el escritor Eager, especialmente en este caso, y luego se eliminan del grupo de búferes.
Martin Smith
7

Para fines de monitoreo, nos gustaría tener una idea aproximada del progreso de esta declaración, mientras se está ejecutando.

¿Uno fuera o en curso?

Si esta es una necesidad que se puede anticipar por adelantado *, puede usar sys.dm_exec_query_profiles

Conexión 1 (sesión 55)

SET STATISTICS XML ON

SELECT so1.*
INTO   dbo.TestDump
FROM   sys.all_objects so1
CROSS JOIN sys.all_objects so2
CROSS JOIN sys.all_objects so3
CROSS JOIN sys.all_objects so4
CROSS JOIN sys.all_objects so5;

Conexión 2

select row_count
from sys.dm_exec_query_profiles
WHERE physical_operator_name = 'Table Insert' 
    AND session_id = 55;

Es posible que deba sumar los recuentos de filas devueltos si SELECT INTOestá utilizando paralelismo .

* La sesión que desea monitorear usando este DMV debe estar habilitada para la recopilación de estadísticas usando SET STATISTICS PROFILE ONo SET STATISTICS XML ON. Solicitar un plan de ejecución "real" de SSMS también funciona (porque establece la última opción).

Martin Smith
fuente
Parece que olvidé hacer +1 en febrero, pero no lo olvidé por completo :). Acabo de usarlo en esta pregunta relacionada, ya que ese OP es al menos en 2014: dba.stackexchange.com/questions/139191/… Gracias por señalar esto; es un DMV bastante útil :-)
Solomon Rutzky
2
@srutzky sí, es muy útil. Y se utilizó en los planes de ejecución en vivo de SSMS 2016 msdn.microsoft.com/en-gb/library/dn831878.aspx
Martin Smith
5

No creo que haya una manera de obtener recuentos de filas, pero puede estimar la cantidad de datos escritos mirando:

SELECT writes 
  FROM sys.dm_exec_requests WHERE session_id = <x>;

SELECT COUNT(*) FROM sys.dm_db_database_page_allocations
(<dbid>, OBJECT_ID(N'dbo.newtablename'), 0, NULL, 'LIMITED');

Si tiene algún tipo de idea de cuántas páginas debería ocupar el montón cuando haya terminado, debería poder calcular el% completado. La última consulta no será rápida a medida que la tabla se agrande. Y probablemente sea más seguro ejecutar lo anterior debajo READ UNCOMMITTED(y no es frecuente que lo recomiendo, para nada).

Aaron Bertrand
fuente
4

Si pudieras cambiar el INSERTde un

SELECT ... INTO DestTable FROM SrcTable

a un

INSERT DestTable SELECT ... FROM SrcTable

entonces su select count(*) from DestTable with (nolock)consulta funcionaría.

Si esto no es posible, puede usar sp_WhoIsActive (o profundizar en los DMV) para controlar cuántas escrituras hace la consulta. Esta sería una medida bastante aproximada, pero podría ser útil si basas el número de escrituras que normalmente hace.

Debería poder obtener un registro mínimo con lo INSERTanterior si agrega WITH (TABLOCK).

James Anderson
fuente
Gracias por este comentario. Queremos obtener un registro mínimo, por eso estamos usando el enfoque SELECT ... INTO (y también porque somos un poco flojos ...)
Dan
1
Debería poder obtener un registro mínimo con lo INSERTanterior si agregaWITH(TABLOCK)
James Anderson
@JamesAnderson: si la tabla se deja como un montón, esto solo provocará el bloqueo nuevamente, ya que se BULK_OPERATIONbloquea.
Martin Smith