Progression de l'instruction SELECT INTO

14

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.

La déclaration ressemble à quelque chose 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 pu trouver un moyen d'obtenir le nombre de lignes affectées sur une donnée transaction_id(quelque chose de similaire à @@ROWCOUNTpeut-être, mais avec l' transaction_idargument as).

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 la déclaration.

Dan
la source
Si vous avez utilisé une table temporaire globale ## TABLE, pourriez-vous effectuer une sélection avec comptage sur la colonne d'index de la ## TABLE pour obtenir le nombre d'enregistrements déjà écrits et approximer le nombre total d'enregistrements à écrire?
CoveGeek

Réponses:

6

Je pense que rowsdans sys.partitionsest 0 en raison de ne pas être encore engagé. 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 rechercher sys.dm_os_buffer_descriptorsces informations:

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 SELECTliste, commentez 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;
Solomon Rutzky
la source
1
C'est créatif. Je veux juste ajouter un avertissement indiquant que l'énumération d'un grand pool de mémoire tampon est très lente.
usr
1
Cela suppose qu'aucune page n'a encore été supprimée du pool de tampons.
Martin Smith
@MartinSmith Les pages peuvent-elles être supprimées avant la validation?
Solomon Rutzky
5
@srutzky - oui. Le journal des transactions contient toutes les informations nécessaires à la restauration. Les pages sales peuvent être écrites sur le disque - par exemple à un point de contrôle ou par le graveur désireux, en particulier dans ce cas, puis supprimées du pool de tampons.
Martin Smith
7

À 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?

Si c'est un besoin qui peut être anticipé à 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 retournées si le parallélismeSELECT INTO est utilisé .

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

Martin Smith
la source
On dirait que j'ai oublié de +1 en février, mais je ne l'ai pas complètement oublié :). Je viens de l'utiliser sur cette question connexe, car cet OP est au moins en 2014: dba.stackexchange.com/questions/139191/… Merci de l'avoir signalé; c'est un DMV très pratique :-)
Solomon Rutzky
2
@srutzky yep c'est très utile. Et utilisé dans les plans d'exécution en direct de SSMS 2016 msdn.microsoft.com/en-gb/library/dn831878.aspx
Martin Smith
5

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 pouvoir 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 READ UNCOMMITTED(et ce n'est pas souvent je le recommande, pour rien).

Aaron Bertrand
la source
4

Si vous pouviez changer INSERTd'un

SELECT ... INTO DestTable FROM SrcTable

à un

INSERT DestTable SELECT ... FROM SrcTable

alors votre select count(*) from DestTable with (nolock)requête 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 plutôt approximative mais pourrait être utile si vous basez le nombre d'écritures qu'il fait normalement.

Vous devriez pouvoir obtenir une journalisation minimale avec ce qui INSERTprécède si vous ajoutez WITH (TABLOCK).

James Anderson
la source
Merci pour ce commentaire. Nous voulons obtenir une journalisation minimale, c'est pourquoi nous utilisons l'approche SELECT ... INTO (et aussi parce que nous sommes un peu paresseux ...)
Dan
1
Vous devriez pouvoir obtenir une journalisation minimale avec ce qui INSERTprécède si vous ajoutezWITH(TABLOCK)
James Anderson
@JamesAnderson - Si la table est laissée comme un tas, cela provoquera à nouveau un blocage car elle prend un BULK_OPERATIONverrou.
Martin Smith