将一个 quickie proc 放在一起以帮助调试,我遇到了一个似乎是编译器错误的问题。
create proc spFoo
@param bit
as
begin
if @param = 0
begin
select *
into #bar
from [master].dbo.spt_values
-- where number between ...
end
else
begin
select top 10 *
into #bar
from [master].dbo.spt_values
order by newid();
end;
end;
尝试以上操作会返回以下错误
消息 2714,级别 16,状态 1,过程 spFoo,第 19 行
数据库中已经有一个名为“#bar”的对象。
在人类可读的意义上,proc 看起来很好:只有一个select into
语句会被执行,因为它们被包裹在if-else
块中。不过很好,SQL Server 无法确认这些语句在逻辑上是相互排除的。也许更令人困惑的是,当drop table #foo
被放置在 if-else 块中时,错误仍然存在(假设会告诉编译器取消分配对象名称),如下所示。
create proc spFoo
@param bit
as
begin
select top 1 *
into #bar
from [master].dbo.spt_values
if @param = 0
begin
drop table #bar;
select *
into #bar
from [master].dbo.spt_values
-- where number between ...
end
else
begin
drop table #bar;
select top 10 *
into #bar
from [master].dbo.spt_values
order by newid();
end;
end;
proc 本身很好。我吸收了它并编写了create table #foo( ... )
andinsert #foo ( ... )
语句,我一直试图跳过select * into
语法。在这一点上,我只是想理解为什么编译器用懒惰的语法对我不利。我唯一能想到的是 DDL 命令保留对象名称IN TEMPDB。
为什么是粗体字?
create proc spIck
as
begin
create table #ack ( col1 int );
drop table #ack;
create table #ack ( colA char( 1 ) );
drop table #ack;
end;
这将失败并显示与上述相同的错误代码。但是下面...
create proc spIck
as
begin
create table ack ( col1 int );
drop table ack;
create table ack ( colA char( 1 ) );
drop table ack;
end;
...成功。上面的原始 proc 尝试也是如此。所以...
我的问题是
TempDB
与用户数据库相比,对象的对象名称保留有什么区别(以及为什么存在) 。我看过的逻辑查询处理参考和 DDL 命令参考都没有解释这一点。
这与 TempDB 中的对象名称保留无关,也与运行时无关。这只是解析器无法遵循确保您的代码不可能尝试创建该表两次的逻辑或代码路径。Ctrl请注意,如果您只是单击解析按钮 ( + ) ,您会得到完全相同的(非运行时!)错误F5。基本上,如果你有这个:
解析器看到这个:
为什么它不适用于实际表,包括在 TempDB 中创建的实际用户表(请注意,它也不是特定于数据库的)?我可以建议的唯一答案是解析器对#temp 表有一套不同的规则(也有很多其他差异)。如果你想要更具体的原因,你需要向 Microsoft 提出一个案例,看看他们是否会给你任何进一步的细节。我猜你会被告知:“这就是它的工作方式。”
这些答案中的更多信息: