Kemajuan pernyataan SELECT INTO

14

Alur ETL kami memiliki SELECT INTO-statement yang sudah berjalan lama, yang membuat tabel dengan cepat, dan mengisinya dengan beberapa ratus juta catatan.

Pernyataan itu terlihat seperti SELECT ... INTO DestTable FROM SrcTable

Untuk tujuan pemantauan, kami ingin mendapatkan gambaran kasar tentang kemajuan pernyataan ini, ketika sedang dieksekusi (kira-kira jumlah baris, jumlah byte tertulis, atau serupa).

Kami mencoba yang berikut ini tanpa hasil:

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

Lebih jauh, kita bisa melihat transaksi dalam sys.dm_tran_active_transactions, tetapi saya tidak dapat menemukan cara untuk mendapatkan jumlah baris yang terpengaruh pada diberikan transaction_id(sesuatu yang mirip dengan @@ROWCOUNTmungkin, tetapi dengan transaction_idargumen sebagai).

Saya mengerti bahwa pada SQL Server SELECT INTO-statement adalah pernyataan DDL dan DML dalam satu, dan karenanya, pembuatan tabel implisit akan menjadi operasi penguncian. Saya masih berpikir harus ada cara pintar untuk mendapatkan semacam informasi kemajuan saat pernyataan sedang berjalan.

Dan
sumber
Jika Anda menggunakan tabel temp global ## TABEL, dapatkah Anda melakukan Pilih dengan hitungan pada kolom indeks pada ## TABEL untuk mendapatkan jumlah catatan yang sudah ditulis dan memperkirakan jumlah catatan dalam total yang akan ditulis?
CoveGeek

Jawaban:

6

Saya curiga bahwa rowsdalam sys.partitions0 karena belum berkomitmen. Tetapi ini tidak berarti bahwa SQL Server tidak mengetahui apa yang akan terjadi jika Transaksi melakukan. Kuncinya adalah mengingat bahwa semua operasi melewati Buffer Pool (yaitu memori) terlebih dahulu, terlepas dari COMMIT atau ROLLBACK dari operasi. Karenanya, kita dapat mencari sys.dm_os_buffer_descriptorsinfo itu:

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

Jika Anda ingin melihat detailnya, batalkan komentar pada baris pertama item dalam SELECTdaftar, beri komentar pada 3 baris yang tersisa.

Saya menguji dengan menjalankan berikut ini di satu Sesi dan kemudian berulang kali menjalankan kueri di atas di lain.

SELECT so1.*
INTO   dbo.TestDump
FROM   sys.objects so1
CROSS JOIN sys.objects so2
CROSS JOIN sys.objects so3;
Solomon Rutzky
sumber
1
Ini kreatif. Hanya ingin menambahkan peringatan bahwa penghitungan kumpulan buffer besar sangat lambat.
usr
1
Ini mengasumsikan bahwa belum ada halaman yang diusir dari kumpulan buffer.
Martin Smith
@MartinSmith Bisakah halaman digusur sebelum komit?
Solomon Rutzky
5
@rutzky - ya. Log transaksi memiliki semua info yang diperlukan untuk mengembalikan. Halaman-halaman yang kotor dapat ditulis ke disk - misalnya di sebuah pos pemeriksaan atau oleh penulis Eager terutama dalam kasus ini kemudian dihapus dari kumpulan buffer.
Martin Smith
7

Untuk tujuan pemantauan, kami ingin mendapat gambaran kasar tentang perkembangan pernyataan ini, ketika sedang dieksekusi.

Satu atau tidak aktif?

Jika ini adalah kebutuhan yang dapat diantisipasi sebelumnya * Anda bisa menggunakan sys.dm_exec_query_profiles

Koneksi 1 (sesi 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;

Koneksi 2

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

Anda mungkin perlu untuk jumlah baris jumlah kembali jika SELECT INTOsudah menggunakan paralelisme .

* Sesi yang ingin Anda monitor menggunakan DMV ini harus diaktifkan untuk pengumpulan statistik menggunakan SET STATISTICS PROFILE ONatau SET STATISTICS XML ON. Meminta rencana eksekusi "aktual" dari SSMS juga berfungsi (karena menetapkan opsi yang terakhir).

Martin Smith
sumber
Sepertinya saya lupa memberi ini +1 pada Feb, tapi saya tidak melupakannya sama sekali :). Saya baru saja menggunakannya pada Pertanyaan terkait ini karena OP setidaknya ada pada 2014: dba.stackexchange.com/questions/139191/... Terima kasih telah menunjukkan ini; itu adalah DMV yang cukup praktis :-)
Solomon Rutzky
2
@srutzky ya itu sangat berguna. Dan memanfaatkan dalam rencana pelaksanaan langsung SSMS 2016 msdn.microsoft.com/en-gb/library/dn831878.aspx
Martin Smith
5

Saya tidak berpikir ada cara untuk mendapatkan jumlah baris, tetapi Anda dapat memperkirakan jumlah data yang ditulis dengan melihat:

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

Jika Anda memiliki beberapa gagasan tentang berapa banyak halaman yang harus diambil ketika selesai, Anda harus dapat menyelesaikan%. Kueri yang terakhir tidak akan cepat karena tabel semakin besar. Dan mungkin paling aman untuk menjalankan di bawah READ UNCOMMITTED(dan tidak sering saya merekomendasikan itu, untuk apa pun).

Aaron Bertrand
sumber
4

Jika Anda dapat mengubah INSERTdari

SELECT ... INTO DestTable FROM SrcTable

ke a

INSERT DestTable SELECT ... FROM SrcTable

maka select count(*) from DestTable with (nolock)permintaan Anda akan berhasil.

Jika ini tidak memungkinkan maka Anda dapat menggunakan sp_WhoIsActive (atau mempelajari DMV) untuk memantau berapa banyak penulisan yang dilakukan kueri. Ini akan menjadi ukuran yang agak kasar tetapi bisa berguna jika Anda mendasarkan jumlah tulisan yang biasanya.

Anda harus bisa mendapatkan minimal logging dengan yang di INSERTatas jika Anda menambahkan WITH (TABLOCK).

James Anderson
sumber
Terima kasih atas komentar ini. Kami ingin mendapatkan penebangan minimal, itulah sebabnya kami menggunakan SELECT ... INTO (dan juga karena kami agak malas ...)
Dan
1
Anda harus bisa mendapatkan minimal logging dengan yang di INSERTatas jika Anda menambahkanWITH(TABLOCK)
James Anderson
@JamesAnderson - Jika tabel dibiarkan sebagai tumpukan, ini hanya akan menyebabkan pemblokiran lagi karena membutuhkan BULK_OPERATIONkunci.
Martin Smith