Avanzamento dell'istruzione SELECT INTO


14

Il nostro flusso ETL ha un'istruzione SELECT INTO di lunga durata, che sta creando una tabella al volo e la popola con diverse centinaia di milioni di record.

La dichiarazione sembra qualcosa del genere SELECT ... INTO DestTable FROM SrcTable

A fini di monitoraggio, vorremmo avere un'idea approssimativa dello stato di avanzamento di questa istruzione, mentre è in esecuzione (numero di righe approssimativo, numero di byte scritti o simili).

Abbiamo provato inutilmente quanto segue:

-- 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')

Inoltre, possiamo vedere la transazione sys.dm_tran_active_transactions, ma non sono stato in grado di trovare un modo per ottenere il conteggio delle righe interessate su un dato transaction_id(qualcosa di simile @@ROWCOUNTforse, ma con l' transaction_idargomento as).

Comprendo che su SQL Server l'istruzione SELECT INTO è sia un'istruzione DDL che un'istruzione DML in una, e come tale, la creazione implicita della tabella sarà un'operazione di blocco. Penso ancora che ci debba essere un modo intelligente per ottenere qualche tipo di informazione sui progressi mentre la dichiarazione è in esecuzione.


Se si utilizzava una tabella temporanea globale ## TABELLA, è possibile eseguire una selezione con conteggio sulla colonna dell'indice in ## TABELLA per ottenere il numero di record già scritti e approssimare la quantità di record in totale da scrivere?
CoveGeek

Risposte:


6

Ho il sospetto che rowsin sys.partitionssia 0 perché non è stato ancora commesso. Ma questo non significa che SQL Server non sia a conoscenza di cosa andrà lì se la Transazione commette. La chiave sta nel ricordare che tutte le operazioni passano prima attraverso il pool di buffer (cioè la memoria), indipendentemente da COMMIT o ROLLBACK dell'operazione. Quindi, possiamo cercare sys.dm_os_buffer_descriptorsquelle informazioni:

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')

Se vuoi vedere i dettagli, decommenta la prima riga di elementi SELECTnell'elenco, commenta le restanti 3 righe.

Ho provato eseguendo quanto segue in una sessione e quindi ripetutamente eseguendo la query sopra in un'altra.

SELECT so1.*
INTO   dbo.TestDump
FROM   sys.objects so1
CROSS JOIN sys.objects so2
CROSS JOIN sys.objects so3;

1
Questo è creativo Voglio solo aggiungere un avviso che enumerare un grande pool di buffer è molto lento.
usr

1
Ciò presuppone che nessuna pool sia stata ancora sfrattata dal pool di buffer.
Martin Smith,

@MartinSmith È possibile sfrattare le pagine prima del commit?
Solomon Rutzky,

5
@srutzky - sì. Il registro delle transazioni contiene tutte le informazioni necessarie per il rollback. Le pagine sporche possono essere scritte su disco - ad esempio in un checkpoint o dallo scrittore Eager, specialmente in questo caso, quindi rimosse dal pool di buffer.
Martin Smith,

7

A fini di monitoraggio, vorremmo avere un'idea approssimativa dello stato di avanzamento di questa affermazione, mentre è in esecuzione.

Una tantum o in corso?

Se questo è un bisogno che può essere anticipato in anticipo * è possibile utilizzare sys.dm_exec_query_profiles

Connessione 1 (sessione 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;

Connessione 2

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

Potrebbe essere necessario sommare le righe conta restituiti se la SELECT INTOsta utilizzando il parallelismo .

* La sessione che si desidera monitorare utilizzando questo DMV deve essere abilitata per la raccolta delle statistiche utilizzando SET STATISTICS PROFILE ONo SET STATISTICS XML ON. Anche la richiesta di un piano di esecuzione "reale" da SSMS funziona (perché imposta quest'ultima opzione).


Sembra che ho dimenticato di fare +1 su questo a febbraio, ma non me ne sono dimenticato del tutto :). Ne ho appena fatto uso su questa domanda correlata poiché il PO è almeno per il 2014: dba.stackexchange.com/questions/139191/… Grazie per averlo sottolineato; è abbastanza utile DMV :-)
Solomon Rutzky

2
@srutzky sì, è molto utile. E utilizzato nei piani di esecuzione live di SSMS 2016 msdn.microsoft.com/en-gb/library/dn831878.aspx
Martin Smith,

5

Non credo che ci sia un modo per ottenere il conteggio delle righe, ma puoi stimare la quantità di dati scritti guardando:

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');

Se hai qualche idea di quante pagine l'heap dovrebbe occupare una volta terminato, dovresti essere in grado di elaborare% completo. Quest'ultima query non sarà veloce man mano che la tabella diventa più grande. E probabilmente il più sicuro per eseguire quanto sopra READ UNCOMMITTED(e non è spesso lo consiglio, per niente).


4

Se potessi cambiare il INSERTda a

SELECT ... INTO DestTable FROM SrcTable

a a

INSERT DestTable SELECT ... FROM SrcTable

quindi la tua select count(*) from DestTable with (nolock)query funzionerebbe.

Se ciò non è possibile, è possibile utilizzare sp_WhoIsActive (o approfondire i DMV) per monitorare il numero di scritture eseguite dalla query. Questo sarebbe un indicatore piuttosto approssimativo, ma potrebbe essere utile se la base è allineata al numero di scritture che fa normalmente.

Dovresti essere in grado di ottenere una registrazione minima con quanto INSERTsopra se aggiungi WITH (TABLOCK).


Grazie per questo commento Vogliamo ottenere una registrazione minima, motivo per cui stiamo usando l'approccio SELECT ... INTO (e anche perché siamo un po 'pigri ...)
Dan

1
Dovresti essere in grado di ottenere una registrazione minima con quanto INSERTsopra se aggiungiWITH(TABLOCK)
James Anderson,

@JamesAnderson - Se la tabella viene lasciata come un heap, questo causerà nuovamente il blocco in quanto richiede un BULK_OPERATIONblocco.
Martin Smith,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.