用户数据库中以#开头的SQL Server表名,不是tempdb,不是临时表

tha*_*guy 13 sql-server sql-server-2008-r2 temporary-tables

不知何故,几十年前,在我们的数据库中创建了一个以#. 它显示在应用程序数据库下的对象资源管理器中,而不是tempdb. 出于某种原因,Azure 不会像这样导入数据库。

我们不能删除它、重命名它或与之交互。我已经尝试过从对象资源管理器中删除脚本删除、从 GUI 中重命名,但都没有奏效。

我们使用的是 SQL 2008 R2。

drop table [*app*].[dbo]."#OBSOLETE";

Database name '*app*' ignored, referencing object in tempdb.
Msg 3701, Level 11, State 5, Line 1
Cannot drop the table '#OBSOLETE', because it does not exist or you do not 
have permission.

exec sp_rename "dbo.#OBSOLETE", "dbo.obsolete"

Msg 15225, Level 11, State 1, Procedure sp_rename, Line 338
No item by the name of 'dbo.#OBSOLETE' could be found in the current database '*app*', given that @itemtype was input as '(null)'.
Run Code Online (Sandbox Code Playgroud)

我们如何杀死这个对象,以便我们可以将它迁移到 Azure?

Sol*_*zky 16

鉴于:

  1. sp_rename 使用对象名称而不是对象 ID,
  2. 我们不能使用对象名称,因为它以 a 开头#并且被解释为具有特殊含义并且处理方式不同,
  3. 所有其他选项都已用尽

您应该尝试通过专用管理控制台 (DAC) 连接直接编辑底层系统目录表:

  1. 得到那张object_id桌子的。
  2. 以单用户模式重启实例。这是为了能够直接更新系统表(即不需要使用 DAC 连接)。
  3. 通过专用管理控制台连接进行连接。您可以通过在命令提示符窗口中运行以下命令在 SQLCMD 交互式会话中执行此操作:

    C:\> SQLCMD -A -E
    
    Run Code Online (Sandbox Code Playgroud)

    或者,使用以下命令直接连接到数据库:

    C:\> SQLCMD -A -E -d {database_name}
    
    Run Code Online (Sandbox Code Playgroud)
  4. 在该数据库中,尝试如下操作:

    UPDATE sys.objects$ {enter}
    SET [name] = N'obsolete' {enter}
    WHERE [object_id] = {ye_olde_object_id}; {enter}
    GO {enter}
    
    Run Code Online (Sandbox Code Playgroud)

    在您放入GO {enter}.

直接编辑系统目录表时请小心,不要太习惯这样做。只有在绝对没有其他解决问题的方法时才应该这样做(例如这里的情况)。避免直接编辑的原因可能有多种,但最初想到的两个原因是:

  • 与我们创建的数据模型非常相似,可能存在我们不知道的关于事物如何工作的规则和工作流(例如,非规范化、管理跨不同表的数据状态的“业务”规则等)
  • 如果您遇到问题并签订了支持合同(我没有看到支持协议的条款,但我很难相信这种语言不会出现在那里)

    @Paul Randal在对我的相关答案的评论中证实:“手动编辑系统表不可撤销地在数据库的引导页面中设置了一个标志,将您的数据库标记为已以这种方式编辑过,而 CSS 可能决定不提供帮助如果您随后遇到该数据库的问题,您就可以了。”

  • 我喜欢这个答案,但也许值得添加一句话来说明为什么这样做如此危险? (4认同)
  • @JoeObbish 我刚刚添加了一些东西。够了吗? (2认同)