SELECT INTO 是否在运行时之前在 TempDB 中保留 #Object 名称?

Pet*_*ier 8 sql-server ddl tempdb temporary-tables

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

尝试上述返回以下错误

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

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

这失败了,错误代码与上面相同。但是以下...

create proc spIck
as
begin
    create table ack ( col1 int );
    drop table ack;
    create table ack ( colA char( 1 ) );
    drop table ack;
end;
Run Code Online (Sandbox Code Playgroud)

……成功了。上面的原始 proc 尝试也是如此。所以...

我的问题是这个

TempDB与用户数据库相比,对象的对象名称保留有什么区别(以及为什么存在)。我查看过的 Logical Query Processing 参考资料和 DDL 命令参考资料似乎都没有解释这一点。

Aar*_*and 6

这与 TempDB 中的对象名称保留无关,也与运行时无关。这只是解析器无法遵循逻辑或代码路径,以确保您的代码不可能尝试两次创建该表。请注意,如果您只单击 Parse 按钮​​ ( Ctrl+ F5) ,您会得到完全相同的(非运行时!)错误。基本上,如果你有这个:

IF 1=1 
  CREATE TABLE #foo(id1 INT);
ELSE
  CREATE TABLE #foo(id2 INT);
Run Code Online (Sandbox Code Playgroud)

解析器看到这个:

  CREATE TABLE #foo(id1 INT);
  CREATE TABLE #foo(id2 INT);
Run Code Online (Sandbox Code Playgroud)

为什么它不适用于实际表,包括在 TempDB 中创建的实际用户表(请注意,它也不是特定于数据库的)?我可以建议的唯一答案是解析器对 #temp 表有一组不同的规则(也有很多其他差​​异)。如果你想要更具体的原因,你需要向微软开一个案例,看看他们是否会给你更多的细节。我猜你会被告知:“这就是它的工作方式。”

这些答案中的更多信息: