Notre flux ETL a une instruction SELECT INTO de longue durée, qui crée une table à la volée et la remplit de plusieurs centaines de millions d'enregistrements.
L'instruction ressemble à SELECT ... INTO DestTable FROM SrcTable
À des fins de surveillance, nous aimerions avoir une idée approximative de la progression de cette instruction pendant son exécution (approximativement nombre de lignes, nombre écrit d'octets ou similaire).
Nous avons essayé ce qui suit en vain:
-- 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')
De plus, nous pouvons voir la transaction dans sys.dm_tran_active_transactions
, mais je n'ai pas trouvé de moyen d'obtenir le nombre de lignes affectées sur un transaction_id
(quelque chose de similaire à @@ROWCOUNT
peut-être, mais avec le transaction_id
comme argument).
Je comprends que sur SQL Server, l'instruction SELECT INTO est à la fois une instruction DDL et une instruction DML, et en tant que telle, la création de table implicite sera une opération de verrouillage. Je pense toujours qu'il doit y avoir un moyen intelligent d'obtenir une sorte d'informations sur la progression pendant l'exécution de l'instruction.
Je soupçonne que rows
dans sys.partitions
est 0 car il n'a pas encore été validé. Mais cela ne signifie pas que SQL Server ne sait pas ce qui se passera si la transaction est validée. La clé est de se rappeler que toutes les opérations passent d'abord par le pool de tampons (c'est-à-dire la mémoire), indépendamment de COMMIT ou ROLLBACK de l'opération. Par conséquent, nous pouvons regarder dans sys.dm_os_buffer_descriptors
pour cette info:
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 vous souhaitez voir les détails, décommentez la première ligne d'éléments de la liste SELECT
, mettez en commentaire les 3 lignes restantes.
J'ai testé en exécutant ce qui suit dans une session, puis en exécutant à plusieurs reprises la requête ci-dessus dans une autre.
SELECT so1.*
INTO dbo.TestDump
FROM sys.objects so1
CROSS JOIN sys.objects so2
CROSS JOIN sys.objects so3;
À des fins de suivi, nous aimerions avoir une idée approximative de la progression de cette déclaration pendant son exécution.
Unique ou en cours?
S'il s'agit d'un besoin prévisible à l'avance *, vous pouvez utiliser sys.dm_exec_query_profiles
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;
select row_count
from sys.dm_exec_query_profiles
WHERE physical_operator_name = 'Table Insert'
AND session_id = 55;
Vous devrez peut-être additionner le nombre de lignes renvoyées si le SELECT INTO
est en utilisant le parallélisme .
* La session que vous souhaitez surveiller à l'aide de ce DMV doit être activée pour la collecte de statistiques à l'aide de SET STATISTICS PROFILE ON
ou SET STATISTICS XML ON
. La demande d'un plan d'exécution "réel" à SSMS fonctionne également (car elle définit la dernière option).
Je ne pense pas qu'il existe un moyen d'obtenir le nombre de lignes, mais vous pouvez estimer la quantité de données écrites en regardant:
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 vous avez une idée du nombre de pages que le tas doit occuper une fois terminé, vous devriez être en mesure de calculer% complet. Cette dernière requête ne sera pas rapide car la table s'agrandit. Et probablement le plus sûr pour exécuter ce qui précède sous READ UNCOMMITTED
(et ce n'est pas souvent je le recommande, pour rien).
Si vous pouviez changer le INSERT
d'un
SELECT ... INTO DestTable FROM SrcTable
à un
INSERT DestTable SELECT ... FROM SrcTable
alors votre requête select count(*) from DestTable with (nolock)
fonctionnerait.
Si cela n'est pas possible, vous pouvez utiliser sp_WhoIsActive (ou plonger dans les DMV) pour surveiller le nombre d'écritures de la requête. Ce serait une jauge assez approximative mais pourrait être utile si vous basez le nombre d'écritures qu'il fait normalement.
Vous devriez pouvoir obtenir journalisation minimale avec le INSERT
ci-dessus si vous ajoutez WITH (TABLOCK)
.