49

我正在使用 SQL Server 2000,它的许多存储过程都广泛使用临时表。数据库有很多流量,我担心创建和删除临时表的线程安全。

假设我有一个创建一些临时表的存储过程,它甚至可以将临时表连接到其他临时表等。还可以说两个用户同时执行存储过程。

  • 一个用户是否可以运行 sp 并创建一个名为 #temp 的临时表,而另一个用户运行相同的 sp 但由于数据库中已经存在一个名为 #temp 的表而被停止?

  • 如果同一个用户在同一个连接上执行同一个存储过程两次呢?

  • 是否有任何其他奇怪的场景可能导致两个用户查询相互干扰?

4

9 回答 9

38

对于第一种情况,不,这是不可能的,因为 #temp 是一个本地临时表,因此对其他连接不可见(假设您的用户正在使用单独的数据库连接)。临时表名称是生成的随机名称的别名,您在引用本地临时表时引用该名称。

在您的情况下,由于您在存储过程中创建本地临时表,因此在退出过程范围时将删除该临时表(请参阅“备注部分”)。

当存储过程完成时,会自动删除在存储过程中创建的本地临时表。该表可以被创建该表的存储过程执行的任何嵌套存储过程引用。调用创建该表的存储过程的进程无法引用该表。

对于第二种情况,是的,您将收到此错误,因为表已经存在,并且只要连接存在,表就会持续存在。如果是这种情况,那么我建议您在尝试创建表之前检查表是否存在。

于 2009-01-21T20:59:05.223 回答
9

本地范围的临时表(带有单个 #)在它们的末尾使用标识符创建,这使得它们是唯一的;多个呼叫者(即使使用相同的登录名)永远不应重叠。

(试试看:从两个连接和相同的登录创建相同的临时表。然后查询 tempdb.dbo.sysobjects 以查看创建的实际表...)

于 2009-01-21T21:00:23.613 回答
5

本地临时表是线程安全的,因为它们只存在于当前上下文中。请不要将上下文与当前连接混淆(来自MSDN:“在存储过程中创建的本地临时表在存储过程完成时会自动删除”),同一个连接可以安全地调用两次或多次创建的存储过程本地临时表(如#TMP)。

您可以通过从两个连接执行以下存储过程来测试此行为。该 SP 将等待 30 秒,因此我们可以确定两个线程将同时在它们自己的 #TMP 表版本上运行:

CREATE PROCEDURE myProc(@n INT)
AS BEGIN
    RAISERROR('running with (%d)', 0, 1, @n);
    CREATE TABLE #TMP(n INT);
    INSERT #TMP VALUES(@n);
    INSERT #TMP VALUES(@n * 10);
    INSERT #TMP VALUES(@n * 100);
    WAITFOR DELAY '00:00:30';
    SELECT * FROM #TMP;
END;
于 2012-05-08T18:20:06.510 回答
5

简短的回答是:

每个查询都保证了临时表的隔离,并且在线程、锁或并发访问方面没有什么可担心的。

我不确定为什么这里的答案谈论“连接”和线程的重要性,因为它们是编程概念,而查询隔离是在数据库级别处理的

本地临时对象在 SQL Server 中由Session分隔。如果您有两个查询同时运行,那么它们是两个完全独立的会话,不会相互干扰。登录无关紧要,例如,如果您使用 ADO.NET 使用单个连接字符串(这意味着多个并发查询将使用相同的 SQL 服务器“登录”),您的查询仍将在单独的会话中运行。连接池也无关紧要。本地临时对象(表存储过程)完全不会被其他会话看到

澄清这是如何工作的;虽然您的代码对本地临时对象有一个单一的通用名称,但 SQL Server 会为每个会话的每个对象附加一个唯一的字符串,以使它们分开。您可以通过在 SSMS 中运行以下命令来查看:

CREATE TABLE #T (Col1 INT)

SELECT * FROM tempdb.sys.tables WHERE [name] LIKE N'#T%';

您将看到类似于以下名称的内容:

T_______________00000000001F

然后,在不关闭该查询选项卡的情况下,打开一个新的查询选项卡并粘贴相同的查询并再次运行它。您现在应该看到如下内容:

T_______________00000000001F

T_______________000000000020

因此,每次您的代码引用#T 时,SQL Server 都会根据会话将其转换为正确的名称。分离都是自动处理的。

于 2017-07-18T04:28:29.390 回答
3

临时表与会话相关联,因此如果不同的用户同时运行您的程序,则不会发生冲突......

于 2009-01-21T20:56:34.247 回答
2

临时表仅在创建它们的查询或过程的上下文中创建。每个新查询都会在数据库上获得一个上下文,该上下文不包含其他查询的临时表。因此,名称冲突不是问题。

于 2009-01-21T20:57:10.157 回答
1

如果您查看 temps 数据库,您可以在那里看到临时表,它们具有系统生成的名称。因此,除了常规的死锁之外,您应该没问题。

于 2009-01-21T20:58:14.790 回答
0

除非您使用两个井号##temp,否则临时表将是本地的,并且仅存在于与用户的本地连接

于 2009-01-21T21:03:14.513 回答
0

首先让我们确保您使用的是真正的临时表,它们是以 # 还是 ## 开头的?如果您在运行中创建实际表,然后反复删除和重新创建它们,您确实会遇到并发用户的问题。如果您正在创建全局临时表(以 ## 开头的临时表),您也可能会遇到问题。如果您不希望出现并发问题,请使用本地临时表(它们以 # 开头)。在 proc 结束时显式关闭它们也是一个很好的做法(或者当您正在谈论长的多步骤 proc 时,proc 不再需要它们)并在创建之前检查是否存在(如果存在则删除) .

于 2009-01-21T21:17:31.567 回答