【问题标题】:Why does SQL Server thinks a Temp Table already exists when it doesn't?为什么 SQL Server 认为临时表已经存在而它不存在?
【发布时间】:2011-01-28 12:35:02
【问题描述】:

背景:有一个存储过程可以使用给定名称的临时表“填充”。该过程是通用的,因为它检查临时表的架构,然后根据架构执行不同的“东西”。我知道这有点奇怪,但我不愿意更改它,因为它在大多数情况下都可以正常工作,除了......

如果我有一个存储过程,它为同名的临时表创建两个不同的模式。从逻辑上讲,它只根据 IF 的哪个分支创建一个临时表。问题是,当 SQL Server 检查 Sproc 时,它似乎正在评估 IF 的两侧(如果它正在检查 SQL 语法,这是有道理的。)

所以这条 SQL 失败了:

IF (1=1)
BEGIN
    CREATE TABLE #test
    (
        a BIGINT NOT NULL,
        b BIGINT NOT NULL
    )
END 
ELSE
BEGIN
    CREATE TABLE #test
    (
        a BIGINT NOT NULL,
        b BIGINT NOT NULL,
        c BIGINT NOT NULL   
    )   
END

--exec SomeProcedureWhichDoesStuffWith#Test

DROP TABLE #test 

出现以下错误:

消息 2714,第 16 级,状态 1,第 14 行
已经有一个对象名为 数据库中的“#test”。

ifs 中的 drop table 组合(在创建表 DDL 之前或之后)似乎无法满足 sql 检查器的要求。

有什么想法可以做到这一点吗?例如,我可以告诉 SQL 不执行语法检查而直接接受存储过程吗?

【问题讨论】:

    标签: sql-server tsql sql-server-2008-r2


    【解决方案1】:

    这是一个限制。动态 SQL 也不起作用,因为 #tmp 将在新会话中创建并立即丢失。对于如图所示的 EXACT sn-p,它的作用相同

    CREATE TABLE #test
    (
        a BIGINT NOT NULL,
        b BIGINT NOT NULL
    )
    
    IF not (1=1)
        ALTER TABLE #test ADD c BIGINT NOT NULL   
    

    同一批次中不能有两个 CREATE .. #name,但这也适用于一般形式

    IF (1=1)
    BEGIN
        CREATE TABLE #test
        (
            a BIGINT NOT NULL,
            b BIGINT NOT NULL
        );
    END 
    GO
    
    IF NOT (1=1)
    BEGIN
        CREATE TABLE #test
        (
            a BIGINT NOT NULL,
            b BIGINT NOT NULL,
            c BIGINT NOT NULL   
        )
    END
    

    【讨论】:

      【解决方案2】:

      使用完全限定名称代替#test。例如,

      [tempdb].[dbo].[temptable]
      

      我在这里Insert result of executing dynamic query into a table 学到了这个小技巧。

      Intellisense 会抱怨,但您仍然可以创建或更改存储过程。

      完成后,一定要把它放下:

      DROP TABLE [tempdb].[dbo].[temptable]
      

      【讨论】:

        【解决方案3】:

        因为我没有 sql 2008,所以我无法测试它。但是据我所知,这是一个使用临时表明确的解析器问题。普通表也可以正常工作

        要解决这个问题,只需使用逻辑 GO 语句拆分代码

        理想情况下:在创建临时表之前检查临时表是否存在,如果存在则删除它们,触发一次,再次创建临时表,触发一次,然后进行任何后期处理,触发一次,最后再次放下它们并发射最后一枪


        如果上述方法不能为您解决问题,您可能还想尝试使用表变量而不是临时表

        【讨论】:

        • 是的,感谢您提供的信息。不幸的是,它是在存储过程中创建表,所以它必须在同一个批次中(所以在 GO 上是不行的)。
        【解决方案4】:

        你总是可以“作弊”:

        DECLARE @SQL VARCHAR(200)
        
        IF (1=1)
        BEGIN
            SET @SQL = 'CREATE TABLE #Temp     ' +
                       '(                      ' +
                       '    a BIGINT NOT NULL, ' +
                       '    b BIGINT NOT NULL  ' +
                       ')                      '
        END
        ELSE
        BEGIN
            SET @SQL = 'CREATE TABLE #Temp     ' +
                       '(                      ' +
                       '    a BIGINT NOT NULL, ' +
                       '    b BIGINT NOT NULL, ' +
                       '    c BIGINT NOT NULL  ' +
                       ')                      '
        END
        
        EXEC SP_EXECUTESQL @SQL
        

        【讨论】:

        • 这不起作用,因为它会立即超出范围并且不能在外部脚本中使用它需要是一个全局临时表或临时表的所有用法都需要在动态 SQL 中。