MSSQL 表变量识别

Eva*_*ing 6 sql-server sql-server-2019

有什么方法可以识别哪个存储过程在 tempDB 中创建表变量?

我正在查看 sys.dm_db_index_operational_stats 中的 forward_fetch_count,我们有几个表的计数非常大(最大的超过 1.33 亿)。表名以 # 后跟 8 个十六进制字符的形式出现在 tempDB 中。有没有办法将其追溯到原始流程,以便我们修复它?

我们在 Linux 上运行 SQL 2019。

谢谢,埃文

Aar*_*and 9

无法通过变量名称识别 tempdb 元数据中的表变量,因为表名称是使用十六进制名称创建的,并且在运行时映射到变量是在引擎内的某个地方完成的。

但是,如果适合行中的列名中至少有一个足够独特,则您可以在某种程度上进行跟踪。假设我们有一个这样的过程:

USE some_database;
GO

CREATE OR ALTER PROCEDURE dbo.flabbermort
AS
  DECLARE @fooblat table(x int, y int, specific_column_name sysname);
  
  INSERT @fooblat(x,y,specific_column_name) 
    SELECT TOP (100) 1,1,name 
    FROM sys.all_columns;

  WAITFOR DELAY '00:30:00';
GO
Run Code Online (Sandbox Code Playgroud)

继续执行:

EXEC some_database.dbo.flabbermort;
Run Code Online (Sandbox Code Playgroud)

现在,如果我们还不知道有问题的object_id(例如,来自 operation_stats),但我们知道我们有一个表变量问题(在此处跟踪 tempdb 使用情况的其他方法),我们可以通过这种方式找到最大的表(在此 Paul 的帮助下)White post这个 Martin Smith 的回答):

;WITH x AS 
(
  SELECT t.name, t.[object_id], 
    [rows] = SUM(ps.row_count), 
    pages  = SUM(ps.used_page_count)
   FROM tempdb.sys.dm_db_partition_stats AS ps
   INNER JOIN tempdb.sys.tables AS t
   ON ps.[object_id] = t.[object_id]
   WHERE index_id IN (0,1)
     AND t.name LIKE N'#%'
   GROUP BY t.name, t.[object_id]
   HAVING SUM(ps.row_count) > 0
)
SELECT x.*, 
  first_file_id = CONVERT(int, SUBSTRING(au.first_page,6,1) 
    + SUBSTRING(au.first_page,5,1)),
  first_page_id = CONVERT(int, SUBSTRING(au.first_page,4,1) 
    + SUBSTRING(au.first_page,3,1) 
    + SUBSTRING(au.first_page,2,1)
    + SUBSTRING(au.first_page,1,1))
FROM x
INNER JOIN sys.partitions AS p 
  ON x.[object_id] = p.[object_id]
INNER JOIN sys.system_internals_allocation_units AS au 
  ON au.container_id = p.[partition_id]
ORDER BY x.page_count DESC;
Run Code Online (Sandbox Code Playgroud)

这会给你这样的输出:

USE some_database;
GO

CREATE OR ALTER PROCEDURE dbo.flabbermort
AS
  DECLARE @fooblat table(x int, y int, specific_column_name sysname);
  
  INSERT @fooblat(x,y,specific_column_name) 
    SELECT TOP (100) 1,1,name 
    FROM sys.all_columns;

  WAITFOR DELAY '00:30:00';
GO
Run Code Online (Sandbox Code Playgroud)

然后,对于object_id您已经确定的表或列表中最大的表,您可以检查DBCC PAGE输出以查看是否有任何东西可以用来识别调用它的代码(当然,如果您知道代码在存储过程中):

DBCC TRACEON(3604);
DBCC PAGE(tempdb,  6, 32, 3);
-- first_file_id --^  ^^-- first_page_id
Run Code Online (Sandbox Code Playgroud)

这产生了这样的输出(缩写),但只有在创建对象的进程仍在运行时才会这样做(因此waitfor在过程中):

EXEC some_database.dbo.flabbermort;
Run Code Online (Sandbox Code Playgroud)

现在我知道这个问题的表中有一个名为列specific_column_name,这样我就可以设置搜索的使用这样的名称都存储过程(这就是为什么列xy并不十分有用,因为它们会产生大量的误报和不会很容易搜索)。我可以使用我的方便花花公子sp_ineachdb(从GitHUb获取最新版本):

CREATE TABLE #out(db sysname, obj nvarchar(640));

DECLARE @sql nvarchar(max) = N'INSERT #out(db,obj)
  SELECT DB_NAME(), QUOTENAME(s.name) + N''.'' + QUOTENAME(o.name)
    FROM sys.schemas AS s INNER JOIN sys.objects AS o
      ON s.schema_id = o.schema_id
    WHERE object_definition(o.object_id) LIKE N''%specific_column_name%'';';

EXEC master.dbo.sp_ineachdb @sql;

SELECT db, obj FROM #out;
Run Code Online (Sandbox Code Playgroud)

输出:

;WITH x AS 
(
  SELECT t.name, t.[object_id], 
    [rows] = SUM(ps.row_count), 
    pages  = SUM(ps.used_page_count)
   FROM tempdb.sys.dm_db_partition_stats AS ps
   INNER JOIN tempdb.sys.tables AS t
   ON ps.[object_id] = t.[object_id]
   WHERE index_id IN (0,1)
     AND t.name LIKE N'#%'
   GROUP BY t.name, t.[object_id]
   HAVING SUM(ps.row_count) > 0
)
SELECT x.*, 
  first_file_id = CONVERT(int, SUBSTRING(au.first_page,6,1) 
    + SUBSTRING(au.first_page,5,1)),
  first_page_id = CONVERT(int, SUBSTRING(au.first_page,4,1) 
    + SUBSTRING(au.first_page,3,1) 
    + SUBSTRING(au.first_page,2,1)
    + SUBSTRING(au.first_page,1,1))
FROM x
INNER JOIN sys.partitions AS p 
  ON x.[object_id] = p.[object_id]
INNER JOIN sys.system_internals_allocation_units AS au 
  ON au.container_id = p.[partition_id]
ORDER BY x.page_count DESC;
Run Code Online (Sandbox Code Playgroud)

这是非常手动的并且很难自动化,并且确实取决于您在第一页上找到列名或其他数据的能力,这将帮助您缩小范围。您最终可能需要签入多个存储过程,或者最终什么也没有——这意味着您可能必须将此搜索带到应用程序的源代码管理中。

重申一下,优先使用表变量而不是临时表的用例很少,所以我会考虑更改您的代码以使用临时表,这样会更容易跟踪。有关差异的信息,请参阅 Martin Smith 的另一篇文章:


归档时间:

查看次数:

155 次

最近记录:

4 年 前