SQL Server 事件探查器中显示的静默异常



以下 SQL 脚本在 SQL Server Profile 上生成Invalid object name '#temp'异常,但SQL Server Management Studiosqlcmd都不会引发该异常:

create table #temp (id int)
insert into #temp (id) values (1)

我只是通过在打开事件"异常"的情况下运行 SQL Server 事件探查器来捕获它,可以在配置跟踪属性时在"事件选择"选项卡上设置该事件。

由于异常往往会降低服务器的速度,因此我使用表变量尝试了类似的代码:

declare @temp table (id int)
insert into @temp (id) values (1)

上面的代码不仅避免了异常,而且在重复调用时速度更快,通过使用临时表来计算性能损失:

if (db_id('performance_test') is null)
create database performance_test
go
use performance_test
go
/* --------------------------- */
/* stress test with temp table */
/* --------------------------- */
declare 
@i int,
@sql varchar(max),
@start_time datetime,
@end_time datetime
set @i = 0
set @sql = 'create table #temp (id int)' + Char(13) + Char(10) + 'insert into #temp (id) values (1)'
set @start_time = getdate()
while (@i < 10000)
begin
exec (@sql)
set @i = @i + 1
end
set @end_time = getdate()
select [Elapsed milliseconds] = datediff(millisecond, @start_time, @end_time) -- outputs 17090 milliseconds
go
/* ------------------------------- */
/* stress test with table variable */
/* ------------------------------- */
declare 
@i int,
@sql varchar(max),
@start_time datetime,
@end_time datetime
set @i = 0
set @sql = 'declare @temp table (id int)' + char(13) + char(10) + 'insert into @temp (id) values (1)'
set @start_time = getdate()
while (@i < 10000)
begin
exec (@sql)
set @i = @i + 1
end
set @end_time = getdate()
select [Elapsed milliseconds] = datediff(millisecond, @start_time, @end_time) -- outputs 10010 milliseconds

我经常读到本地临时表和表变量可以互换使用(当然,如果使用单个批次),但是我认为上面演示的行为可以证明并非如此。

虽然这有点明显,但值得注意的是,如果我们将create table与不同批次中的insert into分开,则不会引发异常:

create table #temp (id int)
go
insert into #temp (id) values (1)

这个静默异常是SQL Server的错误,还是可以称为"设计功能"的东西?鉴于上面的静默异常,也许始终使用表变量而不是临时表会更好。

PS:我已经在SQL Server 2014和SQL Server 2016 Developer版本上进行了测试,得到了相同的结果。

> 正如@JeroenMostert所指出的,异常"Invalid object name"可能在批量重新编译中得到解决(我不知道)。考虑到"延迟名称解析"过程,这是 SQL Server 社区中的一个已知主题,这是非常有意义的。

下面的第一个链接是我在MSDN上发布的问题,Mohsin_A_Khan回答了这个问题,谈到了SQL Server中的"延迟名称解析"过程。另外两个链接有助于了解它的实际工作原理:

通过创建临时表并立即插入行来获取"无效对象名称">

如何查找导致 SQL Server 探查器跟踪中报告的错误的原因?

延迟名称解析和编译

由于"无效对象名称"是由于重新编译过程而预期的,并且不应通过简单地用表变量替换临时表来避免(再次,如@JeroenMostert所指出的那样),我认为这个问题已经回答了。

最新更新