【问题标题】:Recreate temp table with different columns fails重新创建具有不同列的临时表失败
【发布时间】:2019-10-09 09:51:06
【问题描述】:

在编写脚本时,您经常需要更改临时表中的列。尽管受到drop-table-if-exists 语句的保护,但它失败了,因为列没有改变。为什么?

一个大致说明我的意思的例子,为什么这个脚本不起作用?重新创建 #t 说“数据库中已经有一个名为 '#t' 的对象”时失败。

感觉就像 SQL server 作弊,只清空表而不丢弃它。

if exists (select * from tempdb..sysobjects where id=object_id('tempdb..#t'))
    drop table #t

create table #t (
    a int
)

if exists (select * from tempdb..sysobjects where id=object_id('tempdb..#t'))
    drop table #t

create table #t (
    a int
    ,b int
)

【问题讨论】:

标签: sql sql-server


【解决方案1】:

这是一个解析错误。即使您解析查询 (Ctrl + F5) 而不是执行,也会收到此错误。这是因为同一个批次中有两条同名的create语句,在解析名字的时候表已经存在,第二条create语句失败。

您要么需要两个会话,要么在语句之间放置一个 GO 命令。这是一篇类似的博客文章。 An interesting find about Temp tables in SQL Server

【讨论】:

  • 感谢您的链接。大多数情况下,当更改表的定义并重新运行相同的查询(我的示例有点做作)时,就会出现问题,例如解析器抱怨新列不存在。使用 GO 并不是一个完美的解决方案,因为所有变量都超出了范围 - 除非您在脚本的第一部分中适合所有表管理。
  • “使用 GO 并不是一个完美的解决方案,因为所有变量都超出了范围” 为什么我推荐 sp_executesql,因为变量不会超出范围(并且,是的,它们可以在“动态”语句中引用,因为您可以在 sp_executesql 中对语句进行参数化。
【解决方案2】:

您试图在同一个批次中多次创建同一个对象。 SQL Server 看到了这一点,因此生成了一个编译错误(它甚至在 SQL 实际运行之前就发生了)。

如果您必须在同一批次中执行此操作,您可以使用sp_executesql 在单独的批次中运行语句;避免错误。注意我在这里没有使用以# 为前缀的表,因为# 表只会在sp_executesql 的批次中持续存在。然而,考虑到 OP 在这里向我们展示的内容,我猜我们所拥有的内容过于简化了:

USE Sandbox;
GO


IF EXISTS (SELECT *
           FROM sys.sysobjects
           WHERE id = OBJECT_ID('MyTable'))
    DROP TABLE MyTable;

EXEC sp_executesql N'CREATE TABLE MyTable (a int);';

IF EXISTS (SELECT *
           FROM sys.sysobjects
           WHERE id = OBJECT_ID('MyTable'))
    DROP TABLE MyTable;

EXEC sp_executesql N'CREATE TABLE MyTable (a int, b int);';

GO

DROP TABLE MyTable;

【讨论】:

  • 你会希望解析器比这更智能一点,但显然不是......
  • 正在变得聪明,@JonasWS。显然,尝试批量创建 same 表会导致错误。解析器不会在批处理运行之前评估 SQL 语句;它只是解析它。在语句实际运行之前不会进行评估。
【解决方案3】:

能否添加 GO 语句

if exists (select * from tempdb..sysobjects where id=object_id('tempdb..#t'))
        drop table #t

    create table #t (
        a int
    )
    GO
    if exists (select * from tempdb..sysobjects where id=object_id('tempdb..#t'))
        drop table #t

    create table #t (
        a int
        ,b int
    )

【讨论】:

    【解决方案4】:

    这是因为在解析批处理时,SQL Server 看到你已经创建了临时表#t,所以再次创建它会产生错误。 使用 GO 语句分隔批次

    if exists (select * from tempdb..sysobjects where id=object_id('tempdb..#t'))
        drop table #t
    
    create table #t (
        a int
    )
    
    go
    
    if exists (select * from tempdb..sysobjects where id=object_id('tempdb..#t'))
        drop table #t
    
    create table #t (
        a int
        ,b int
    )
    

    【讨论】:

      【解决方案5】:

      您必须在第二个if 之前使用GO 语句。

      SQL Server Utilities Statements - GO

      在那里你可以阅读:

      SQL Server 实用程序将 GO 解释为它们应该将当前批次的 Transact-SQL 语句发送到 SQL Server 实例的信号。当前批次的语句由自上次 GO 或自特别会话或脚本(如果这是第一个 GO)开始以来输入的所有语句组成。

      你也可以替换你的条件

      if exists (select * from tempdb..sysobjects where id=object_id('tempdb..#t'))
        drop table #t
      

      if object_id('tempdb..#t') is not null
        drop table #t
      

      编辑:替代方法还需要GO

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-19
        • 2017-11-28
        • 2020-10-12
        • 1970-01-01
        • 2020-11-09
        相关资源
        最近更新 更多