SELECT INTO 语句的进展

Dan*_*Dan 15 sql-server insert sql-server-2014

我们的 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')
Run Code Online (Sandbox Code Playgroud)

此外,我们可以在 中看到事务sys.dm_tran_active_transactions,但我无法找到一种方法来获取给定的受影响行数transaction_id(类似于@@ROWCOUNT也许,但带有transaction_idas 参数)。

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

Sol*_*zky 7

由于尚未提交,我怀疑rowsinsys.partitions为 0。但这并不意味着 SQL Server 不知道如果事务确实提交会发生什么。关键是要记住,无论操作是 COMMIT 还是 ROLLBACK,所有操作都首先通过 Buffer Pool(即内存)。因此,我们可以查找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')
Run Code Online (Sandbox Code Playgroud)

如果要查看详细信息,请取消对SELECT列表中第一行项目的注释,将其余 3 行注释掉。

我通过在一个会话中运行以下内容进行测试,然后在另一个会话中重复运行上面的查询。

SELECT so1.*
INTO   dbo.TestDump
FROM   sys.objects so1
CROSS JOIN sys.objects so2
CROSS JOIN sys.objects so3;
Run Code Online (Sandbox Code Playgroud)

  • @srutzky - 是的。事务日志包含回滚所需的所有信息。脏页可以写入磁盘 - 例如在检查点或由 Eager 编写器(尤其是在这种情况下)然后从缓冲池中删除。 (5认同)

Mar*_*ith 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;
Run Code Online (Sandbox Code Playgroud)

连接 2

select row_count
from sys.dm_exec_query_profiles
WHERE physical_operator_name = 'Table Insert' 
    AND session_id = 55;
Run Code Online (Sandbox Code Playgroud)

您可能需要总结,如果返回的行数SELECT INTO使用并行

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

  • @srutzky 是的,它非常有用。并在 SSMS 2016 实时执行计划中使用 https://msdn.microsoft.com/en-gb/library/dn831878.aspx (2认同)

Aar*_*and 6

我认为没有办法获得行数,但您可以通过查看以下内容来估计写入的数据量:

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');
Run Code Online (Sandbox Code Playgroud)

如果您对堆完成后应占用多少页有某种想法,您应该能够计算完成百分比。随着表变大,后一个查询不会很快。并且可能最安全地运行上述内容READ UNCOMMITTED(并且我不经常推荐这样做,对于任何事情)。


Jam*_*son 5

如果你可以改变INSERT

SELECT ... INTO DestTable FROM SrcTable
Run Code Online (Sandbox Code Playgroud)

INSERT DestTable SELECT ... FROM SrcTable
Run Code Online (Sandbox Code Playgroud)

那么您的select count(*) from DestTable with (nolock)查询将起作用。

如果这是不可能的,那么您可以使用 sp_WhoIsActive(或深入研究 DMV)来监视查询执行的写入次数。这将是一个相当粗略的衡量标准,但如果您对它通常所做的写入次数进行基线划定,则可能会很有用。

你应该能够得到最小的记录INSERT上述如果您添加WITH (TABLOCK)