ความคืบหน้าของคำสั่ง SELECT INTO


14

โฟลว์ ETL ของเรามีคำสั่ง SELECT INTO ที่ดำเนินมายาวนานซึ่งสร้างตารางได้ทันทีและเติมข้อมูลด้วยหลายร้อยล้านเรคคอร์ด

คำสั่งมีลักษณะเหมือน SELECT ... INTO DestTable FROM SrcTable

เพื่อจุดประสงค์ในการตรวจสอบเราต้องการทราบคร่าวๆเกี่ยวกับความคืบหน้าของคำสั่งนี้ในขณะที่มันกำลังดำเนินการ (ประมาณ rowcount จำนวนไบต์ที่เขียนหรือคล้ายกัน)

เราได้ลองทำสิ่งต่อไปนี้โดยไม่มีประโยชน์:

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

นอกจากนี้เราสามารถเห็นการทำธุรกรรมในsys.dm_tran_active_transactionsแต่ฉันไม่สามารถหาวิธีที่จะได้รับจำนวนแถวที่ได้รับผลกระทบtransaction_id(สิ่งที่คล้ายกับ@@ROWCOUNTอาจจะ แต่ด้วยtransaction_idอาร์กิวเมนต์เป็น)

ฉันเข้าใจว่าใน SQL Server SELECT INTO นั้นเป็นทั้งคำสั่ง DDL และคำสั่ง DML ในที่เดียวและเช่นนี้การสร้างตารางโดยนัยจะเป็นการดำเนินการล็อค ฉันยังคิดว่าต้องมีวิธีที่ชาญฉลาดในการรับข้อมูลความคืบหน้าบางอย่างในขณะที่คำสั่งกำลังทำงานอยู่


หากคุณใช้ตาราง temp แบบโกลบอล ## TABLE คุณสามารถเลือก Select ด้วย count ในคอลัมน์ index ใน ## TABLE เพื่อรับจำนวนของเร็กคอร์ดที่เขียนไปแล้วและประมาณจำนวนเร็กคอร์ดทั้งหมดที่จะเขียน?
CoveGeek

คำตอบ:


6

ฉันสงสัยว่าrowsในsys.partitionsคือ 0 เนื่องจากยังไม่ได้กระทำ แต่นี่ไม่ได้หมายความว่า SQL Server จะไม่รู้ว่าจะเกิดอะไรขึ้นถ้าธุรกรรมกระทำ กุญแจสำคัญคือในการจดจำว่าการดำเนินการทั้งหมดจะผ่าน Buffer Pool (เช่นหน่วยความจำ) ก่อนโดยไม่คำนึงถึง COMMIT หรือ ROLLBACK ของการดำเนินการ ดังนั้นเราสามารถค้นหาsys.dm_os_buffer_descriptorsข้อมูลดังกล่าวได้:

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

หากคุณต้องการดูรายละเอียดให้ยกเลิกการใส่เครื่องหมายในแถวแรกของรายการในSELECTรายการโดยใส่เครื่องหมาย 3 บรรทัดที่เหลือ

ฉันทดสอบโดยการเรียกใช้สิ่งต่อไปนี้ในหนึ่งเซสชันจากนั้นเรียกใช้แบบสอบถามด้านบนซ้ำอีกครั้ง

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

1
นี่คือความคิดสร้างสรรค์ เพียงแค่ต้องการเพิ่มคำเตือนว่าการแจกแจงบัฟเฟอร์ขนาดใหญ่นั้นช้ามาก
usr

1
นี่ถือว่าสมมติว่าไม่มีหน้าใดถูกขับออกจากพูลบัฟเฟอร์เลย
Martin Smith

@MartinSmith หน้าจะถูกขับไล่ก่อนที่จะกระทำได้หรือไม่?
โซโลมอน Rutzky

5
@srutzky - ใช่ บันทึกธุรกรรมมีข้อมูลทั้งหมดที่จำเป็นในการย้อนกลับ สามารถเขียนหน้าสกปรกลงบนดิสก์ได้เช่นที่จุดตรวจหรือโดยตัวเขียน Eager โดยเฉพาะในกรณีนี้จากนั้นนำออกจากบัฟเฟอร์พูล
Martin Smith

7

เพื่อจุดประสงค์ในการติดตามตรวจสอบเราต้องการทราบคร่าวๆถึงความคืบหน้าของคำแถลงนี้ขณะที่กำลังดำเนินการอยู่

หนึ่งครั้งหรือต่อเนื่อง

หากนี่คือความต้องการที่สามารถคาดการณ์ล่วงหน้า * คุณสามารถใช้ sys.dm_exec_query_profiles

การเชื่อมต่อ 1 (เซสชัน 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;

การเชื่อมต่อ 2

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

คุณอาจจำเป็นต้องสรุปนับแถวกลับถ้าSELECT INTOเป็นใช้ขนาน

* เซสชั่นที่คุณต้องการตรวจสอบการใช้ DMV นี้จะต้องถูกเปิดใช้งานสำหรับคอลเลกชันสถิติการใช้หรือSET STATISTICS PROFILE ON SET STATISTICS XML ONการร้องขอแผนการดำเนินการ "จริง" จาก SSMS ก็ใช้ได้เช่นกัน (เพราะตั้งค่าตัวเลือกหลัง)


ดูเหมือนว่าฉันลืม +1 ในเดือนกุมภาพันธ์นี้ แต่ฉันไม่ลืมเลย :) ฉันเพิ่งใช้มันกับคำถามที่เกี่ยวข้องนี้เนื่องจาก OP อย่างน้อยในปี 2014: dba.stackexchange.com/questions/139191/ขอขอบคุณที่ชี้ให้เห็น มันค่อนข้างมีประโยชน์ DMV :-)
โซโลมอน Rutzky

2
@srutzky ครับมันมีประโยชน์มาก และใช้ประโยชน์จากแผนการดำเนินการสด SSMS 2016 msdn.microsoft.com/en-gb/library/dn831878.aspx
Martin Smith

5

ฉันไม่คิดว่าจะมีวิธีนับแถวได้ แต่คุณสามารถประมาณจำนวนข้อมูลที่เขียนโดยดูที่:

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

หากคุณมีความคิดบางอย่างเกี่ยวกับจำนวนหน้าของ heap ที่ควรใช้เมื่อดำเนินการเสร็จสิ้นคุณควรจะสามารถทำงาน% สมบูรณ์ได้ แบบสอบถามหลังจะไม่เร็วเท่าที่โต๊ะใหญ่ขึ้น และอาจปลอดภัยที่สุดที่จะเรียกใช้ข้างต้นภายใต้READ UNCOMMITTED(และไม่บ่อยครั้งที่ฉันแนะนำสิ่งนั้น)


4

หากคุณสามารถเปลี่ยนINSERTจาก

SELECT ... INTO DestTable FROM SrcTable

เพื่อ

INSERT DestTable SELECT ... FROM SrcTable

จากนั้นselect count(*) from DestTable with (nolock)แบบสอบถามของคุณจะได้ผล

หากไม่สามารถทำได้คุณสามารถใช้ sp_WhoIsActive (หรือเจาะลึกเข้าไปใน DMVs) เพื่อตรวจสอบว่ามีการเขียนแบบสอบถามจำนวนเท่าใด นี่อาจเป็นเกจที่ค่อนข้างหยาบ แต่อาจมีประโยชน์หากคุณตั้งค่าจำนวนการเขียนตามปกติ

คุณควรจะสามารถที่จะได้รับการเข้าสู่ระบบน้อยที่สุดกับข้างต้นถ้าคุณเพิ่มINSERTWITH (TABLOCK)


ขอบคุณสำหรับความคิดเห็นนี้ เราต้องการได้รับการบันทึกที่น้อยที่สุดซึ่งเป็นสาเหตุที่เราใช้วิธี SELECT ... เข้าสู่ (และเพราะเราเป็นคนขี้เกียจ ... )
Dan

1
คุณควรจะได้รับการบันทึกขั้นต่ำเพียงเล็กน้อยINSERTถ้าคุณเพิ่มWITH(TABLOCK)
James Anderson

@JamesAnderson - หากตารางถูกทิ้งให้เป็นกองสิ่งนี้จะทำให้เกิดการบล็อกอีกครั้งเมื่อมีการBULK_OPERATIONล็อก
Martin Smith
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.