2009-01-21 30 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將其轉換爲基於會話的正確名稱。分離都是自動神奇地處理的。