2009-01-21 27 views
42

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

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

  • 是否有可能为一个用户运行SP和创建一个名为#TEMP一个临时表,和其他用户运行相同的SP,但被停止,因为在数据库中存在一个名为#TEMP已经表?

  • 如果同一个用户在同一个连接上执行两次相同的存储过程,那么该怎么办?

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

回答

27

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

对于您的情况,由于您要在存储过程中创建本地临时表,请参阅that temp table will be dropped when the scope of the procedure is exited(请参阅“备注部分”)。

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

+0

如何从一个连接并行执行相同的进程? – SQLMenace 2009-01-21 21:03:11

3

临时表绑在会话,因此,如果不同的用户运行的程序同时有没有冲突?

1

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

1

如果您查看临时数据库,您可以在那里看到临时表,并且它们具有系统生成的名称。所以除了常规的僵局之外,你应该没问题。

6

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

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

0

,除非你使用两个井号##温度临时表将是本地的,只存在于本地连接到用户

0

首先让我们确保您使用的是真正的临时表,它们是否以#或##开头?如果您正在动态创建实际表格,然后重复删除并重新创建表格,则确实会遇到并发用户问题。如果您正在创建全局临时表(以##开头),您也可能遇到问题。如果您不希望并发问题使用本地临时表(它们以#开头)。在proc结束时明确关闭它们(或者当proc不再需要它们时,如果你正在谈论长时间的多步骤procs),并且在创建之前检查是否存在(并且如果是这样) 。

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; 
2

简短的回答是:

临时表的隔离是保证每个查询,并有 没有什么可担心的线程,锁或 并发访问。

我不知道为什么在这里回答说说“关系”和线程,因为这些的意义是编程的概念,而查询隔离在数据库级别处理。

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

澄清这是如何工作的;虽然您的代码具有本地临时对象的单一通用名称,但SQL Server会在每个会话中为每个对象附加一个唯一字符串,以使它们保持分离。

CREATE TABLE #T (Col1 INT) 

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

你会看到类似的名称如下:您可以通过运行在SSMS下面看到这个

T_______________00000000001F

然后,而不关闭该查询选项卡,开辟了新的查询选项卡并粘贴到相同的查询中并再次运行。您现在应该看到类似以下内容:

T_______________00000000001F

T_______________000000000020

因此,每个代码引用#T时,SQL Server将其转换为基于会话的正确名称。分离都是自动神奇地处理的。