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。
谢谢,埃文
无法通过变量名称识别 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
,这样我就可以设置搜索的使用这样的名称都存储过程(这就是为什么列x
和y
并不十分有用,因为它们会产生大量的误报和不会很容易搜索)。我可以使用我的方便花花公子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 次 |
最近记录: |