SELECT INTO语句的进度


14

我们的ETL流具有长期运行的SELECT INTO语句,该语句正在动态创建一个表,并在其中填充了几亿条记录。

该语句看起来像 SELECT ... INTO DestTable FROM SrcTable

为了便于监视,我们希望大致了解该语句在执行过程中的进度(大约行数,写入的字节数或类似的值)。

我们尝试了以下方法,但无济于事:

-- 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_idas参数)。

我知道在SQL Server上,SELECT INTO语句既是DDL语句又是DML语句,因此隐式表的创建将是锁定操作。我仍然认为在语句运行时必须有某种巧妙的方法来获取某种进度信息。


如果使用全局临时表## TABLE,是否可以在## TABLE的索引列上执行带计数的选择,以获取已写入的记录数并近似要写入的记录总数?
CoveGeek '16

Answers:


6

我怀疑rowsin sys.partitions是0,因为尚未提交。但这并不意味着SQL Server不知道如果事务提交了,SQL Server将如何处理。关键在于记住所有操作都首先经过缓冲池(即内存),而不管该操作的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
这确实假定尚未从缓冲池中逐出任何页面。
马丁·史密斯

@MartinSmith提交之前可以驱逐页面吗?
所罗门·鲁茨基

5
@srutzky-是的。事务日志具有回滚所需的所有信息。脏页可以写入磁盘-例如在检查点或由Eager写入器,尤其是在这种情况下,然后将其从缓冲池中删除。
马丁·史密斯,

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则可能需要对返回的行计数求和。

*必须使用SET STATISTICS PROFILE ON或启用要使用此DMV监视的会话以进行统计信息收集SET STATISTICS XML ON。从SSMS请求“实际”执行计划也是可行的(因为它设置了后一个选项)。


似乎我在2月忘记了对此+1,但我并没有完全忘记:)。我只是在此相关问题上使用了它,因为OP至少在2014年发布:dba.stackexchange.com/questions/139191/…感谢您指出这一点;这是非常方便的DMV :-)
所罗门·鲁兹基

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

如果您对完成后堆应该占用多少页有某种想法,那么您应该能够得出%complete的信息。当表变大时,后一个查询将不会很快。可能是最安全的在上面运行READ UNCOMMITTED(并且我通常不建议这样做)。


4

如果您可以INSERT

SELECT ... INTO DestTable FROM SrcTable

INSERT DestTable SELECT ... FROM SrcTable

那么您的select count(*) from DestTable with (nolock)查询就可以了。

如果无法做到这一点,则可以使用sp_WhoIsActive(或深入研究DMV)来监视查询执行了多少写操作。这将是一个相当粗略的衡量标准,但是如果您对它通常执行的写操作次数进行基线计算,则可能会很有用。

如果添加,您应该可以使用上述方法获得最少的日志记录INSERTWITH (TABLOCK)


感谢您的评论。我们希望获得最少的日志记录,这就是为什么我们使用SELECT ... INTO方法(也是因为我们有点懒...)
Dan

1
INSERT如果您添加以下内容,则应该可以使上述内容的日志记录最少WITH(TABLOCK)
James Anderson

@JamesAnderson-如果将表作为堆保留,这将导致再次阻塞,因为它需要BULK_OPERATION锁。
马丁·史密斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.