【问题标题】:Explicitly drop temp table or let SQL Server handle it显式删除临时表或让 SQL Server 处理它
【发布时间】:2011-09-08 12:59:45
【问题描述】:

处理临时表删除的最佳做法是什么。我已经读过你应该明确地处理 drop 并且 sql server 应该处理 drop....什么是正确的方法?我一直认为您应该自己清理在存储过程中创建的临时表等。但是,后来我发现了其他建议。

任何见解将不胜感激。我只是担心我没有遵循我创建的临时表的最佳实践。

谢谢,

S

【问题讨论】:

  • SQL Server 会在本地 #temp 表超出范围时自动删除它们,因此 AFAIK 并不重要。不确定此自动删除是否在范围退出之前同步发生,因此与显式删除相比可能会略有不同,但对于大型表,我认为无论如何都会推迟删除。
  • 任何支持一种或另一种方法(特别是支持显式删除)的论点示例也将不胜感激。一方面,我不能真正证明明确删除是“最佳实践”,也不能说它总是是多余的。另一方面,如果您确实告诉我们您(或其他人对此事的看法在某些时候对您来说似乎是明智的)究竟为什么认为方法比其他方法好。

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


【解决方案1】:

我的观点是,首先看看你是否真的需要一个临时表 - 或者 - 你可以使用公用表表达式 (CTE)。其次,我总是会放弃我的临时表。有时你需要一个临时表的范围为连接(例如##temp),所以如果你第二次运行查询,并且你有明确的代码来创建临时表,你会得到一个错误,说该表已经存在。自行清理始终是一种良好的软件实践。

编辑:2021 年 11 月 3 日

另一种选择是 TABLE 变量,一旦查询完成,它将超出范围:

DECLARE @MyTable AS TABLE (
    MyID INT, 
    MyText NVARCHAR(256)
)

INSERT INTO
    @MyTable
VALUES
    (1, 'One'),
    (2, 'Two'),
    (3, 'Three')

SELECT
    *
FROM
    @MyTable

【讨论】:

  • 请注意##temp 对每个人都是可见的(即其他连接),但会在原始会话结束时被丢弃。
  • 必须在存储过程的底部维护曾经在其中使用过的所有临时表的目录,以便删除它们是额外的样板文件,并且容易出错。自己清理只是在必要时是一种很好的软件实践,并且这样做不会降低整个过程的效率。总是在不了解目的和上下文的情况下做某事,也不知道是否有更好的方法,这就是我们如何变得臃肿难以维护代码的原因。清理是一项消耗资源的任务,也不例外。
  • 或者只使用@table_variables 并在没有样板的情况下正确清理它们。
【解决方案2】:

CREATE TABLE (Transact-SQL)

临时表超出范围时会自动删除,除非使用 DROP TABLE 显式删除:

  • 在存储过程中创建的本地临时表会在存储过程完成后自动删除。该表可以被创建该表的存储过程执行的任何嵌套存储过程引用。调用创建该表的存储过程的进程无法引用该表。
  • 所有其他本地临时表都会在当前会话结束时自动删除。
  • 当创建表的会话结束并且所有其他任务都停止引用它们时,会自动删除全局临时表。任务和表之间的关联仅在单个 Transact-SQL 语句的生命周期内维护。这意味着在创建会话结束时主动引用表的最后一个 Transact-SQL 语句完成后,将删除全局临时表。

【讨论】:

  • 有时您几乎无法控制范围(连接池)
  • @MeggieLuski 这是一种误解,因为当重用池中的连接时调用 sp_reset_connection 会删除临时表。
  • 是的,但有时您无法控制何时运行 sp 重置连接,尤其是当您在 yoru 架构中有多个层时
  • 参考:docs.microsoft.com/en-us/sql/t-sql/statements/… -- "临时表超出范围时会自动删除,除非使用 DROP TABLE 显式删除" -- "在存储过程中创建的本地临时表将被删除存储过程完成时自动执行。创建该表的存储过程执行的任何嵌套存储过程都可以引用该表。"
  • 超出范围时会自动删除 - 这足够准确吗? “超出范围”也可能意味着“在查询编辑器窗口中按 F5 后脚本已完成执行”,在这种情况下临时表仍然存在。 @kol 的链接文章特别指出:“所有其他本地临时表都会在当前会话结束时自动删除”,这比简单的“超出范围”要具体得多。
【解决方案3】:

我曾经陷入让后台服务器进程清理对象的人群中,但是最近遇到的 TempDB 日志文件过度增长的问题改变了我的看法。我不确定每个版本的 SQL Server 是否一直都是这种情况,但是自从迁移到 SQL 2016 并将驱动器放在 PureStorage SSD 阵列上之后,事情的运行方式就有些不同了。进程通常受 CPU 限制而不是 I/O 限制,并且显式删除临时对象不会导致日志增长问题。虽然我还没有深入研究原因,但我怀疑这与 .NET 世界中的垃圾收集没有什么不同,在显式调用时它是同步的,而在留给系统时它是异步的。这很重要,因为显式删除将释放日志文件中的存储,并使其在下一次日志备份时可用,而在不显式删除对象时似乎并非如此。在大多数系统上,这可能不是什么大问题,但在支持大量 ERP 和具有许多并发事务的 Web 店面以及大量使用 TempDB 的系统上,它产生了很大的影响。至于为什么首先创建 TempDB 对象,由于大多数查询中的数据量,它无论如何都会溢出到 TempDB 存储中,因此创建具有必要索引的对象通常比让系统自动处理。

【讨论】:

    【解决方案4】:

    在每个线程创建自己的一组表并且线程数受到限制的多线程场景中,不删除自己的表意味着调控器将认为您的线程已完成并产生更多线程......但是临时表仍然存在(因此与服务器的连接),因此您将超出州长的限制。如果您手动删除临时表,则线程不会完成,直到它们被删除并且不会产生新线程,从而保持调控器防止压倒 SQL 引擎的能力

    【讨论】:

    • 我觉得这很难理解。
    • @FistOfFury,调控器是一种限制线程创建的设备。一个有利的用例是您可能有一堆任务要在数据库中执行,但您不想让服务器不堪重负......在这种情况下,您有某种 CPU 利用率检测方案(或一个简单的线程限制)并仅根据它产生新线程
    • 我想我明白了。因此,当您有一个进程并行地重复创建临时表时,这是一个特殊的用例,最终会使数据库不堪重负。这是有道理的。
    • 我在这里遗漏了两件事:(1)调控器如何知道某个线程是否没有删除它的临时表?州长不只是一个线程生成器\杀手吗? (2) 即使管理者有这样的知识,是什么让它认为一个线程没有删除它的临时表?不应该反过来吗?
    【解决方案5】:

    根据我的观点。无需显式删除临时表。 SQL server 将处理删除存储在 temp db 中的临时表,以防处理查询的空间不足。

    【讨论】:

    • 当我处理存储过程示例并运行 SQL 时,我在 Microsoft SQL Server 中的经验,它因未清理的错误而“中止”,因此临时表留在那里,所以当我尝试再次运行它,“选择进入”会因为仍然存在的表而被删除。当我运行我的“drop tables”时,其中许多都运行了,所以这些表仍然处于活动状态。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多