web-dev-qa-db-fra.com

Progression de l'instruction SELECT INTO

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.

14
Dan

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;
6
Solomon Rutzky

À 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

Connexion 1 (session 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;

Connexion 2

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

7
Martin Smith

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

5
Aaron Bertrand

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

4
James Anderson