5

在MS SQL Server中,我使用全局臨時表來存儲客戶端傳遞的會話相關信息,然後在觸發器中使用該信息。MS SQL Server - 全局臨時表的安全併發使用?

由於同一個全局臨時表可以在不同的會話中使用,並且在我想寫入時可能存在也可能不存在(取決於先前使用過的所有會話是否關閉),我正在做在寫入之前檢查全局臨時表存在的基礎上創建的。

IF OBJECT_ID('tempdb..##VTT_CONTEXT_INFO_USER_TASK') IS NULL 
    CREATE TABLE ##VTT_CONTEXT_INFO_USER_TASK (
    session_id smallint, 
    login_time datetime, 
    HstryUserName VDT_USERNAME, 
    HstryTaskName VDT_TASKNAME, 
) 

MERGE ##VTT_CONTEXT_INFO_USER_TASK As target 
USING (SELECT @@SPID, @HstryUserName, @HstryTaskName) as source (session_id, HstryUserName, HstryTaskName) 
ON (target.session_id = source.session_id) 
WHEN MATCHED THEN 
    UPDATE SET HstryUserName = source.HstryUserName, HstryTaskName = source.HstryTaskName 
WHEN NOT MATCHED THEN 
    INSERT VALUES (@@SPID, @LoginTime, source.HstryUserName, source.HstryTaskName); 

的問題是,我的檢查表存在和MERGE語句之間,SQL Server可能會刪除臨時表,如果它是使用它發生之前的所有會話中,準確的情況下關閉(這實際上發生在我的測試中)。

是否有關於如何避免這種併發問題最佳做法是,一個表沒有檢查它的存在和它的後續使用之間下降了嗎?

回答

2

我會說,就長期而言,我會按照戈登的建議,即我將採取必要步驟,實行正常的數據庫中的表來存儲它需要在可訪問的客戶端應用程序的信息開始觸發。

但是,由於時間限制(這需要幾周才能獲得新常規表的必要的正式批准),所以我想出了一個解決方案,用於防止SQL Server將全局臨時表檢查其存在以及MERGE聲明。

有當全局臨時表由SQL Server下降了一些信息,在那裏;我的個人測試表明,在創建會話的會話關閉時,SQL Server會刪除一個全局臨時表,並且在其他會話中啓動了其他任何會更改該表中的數據的事務。

我的解決辦法是在我之前也檢查其存在的全局臨時表的假數據的變化。如果該表存在,那麼SQL Server將知道它需要保留它直到當前事務結束,並且在檢查其存在之後它不能再被丟棄。現在的代碼如下所示(正確的評論,因爲這是怎樣的一個黑客):

-- Faking a delete on the table ensures that SQL Server will keep the table until the end of the transaction 
-- Since ##VTT_CONTEXT_INFO_USER_TASK may actually not exist, we need to fake the delete inside TRY .. CATCH 
-- FUTURE 2016, Feb 03: A cleaner solution would use a real table instead of a global temp table. 
BEGIN TRY 
    -- Because schema errors are checked during compile, they cannot be caught using TRY, this can be done by wrapping the query in sp_executesql 
    DECLARE @QueryText NVARCHAR(100) = 'DELETE ##VTT_CONTEXT_INFO_USER_TASK WHERE 0 = 1' 
    EXEC sp_executesql @QueryText 
END TRY 
BEGIN CATCH 
-- nothing to do here (see comment above) 
END CATCH 

IF OBJECT_ID('tempdb..##VTT_CONTEXT_INFO_USER_TASK') IS NULL 
    CREATE TABLE ##VTT_CONTEXT_INFO_USER_TASK (
    session_id smallint, 
    login_time datetime, 
    HstryUserName VDT_USERNAME, 
    HstryTaskName VDT_TASKNAME, 
) 

MERGE ##VTT_CONTEXT_INFO_USER_TASK As target 
USING (SELECT @@SPID, @HstryUserName, @HstryTaskName) as source (session_id, HstryUserName, HstryTaskName) 
ON (target.session_id = source.session_id) 
WHEN MATCHED THEN 
    UPDATE SET HstryUserName = source.HstryUserName, HstryTaskName = source.HstryTaskName 
WHEN NOT MATCHED THEN 
    INSERT VALUES (@@SPID, @LoginTime, source.HstryUserName, source.HstryTaskName); 

雖然我稱之爲「需要您自擔風險使用它」的解決方案,但它防止使用的其他會話中的全局臨時表會影響其在當前會話中的使用,這是讓我開始此線程的關注點。

謝謝你們所有的時間! (從文本格式編輯到回覆)

4

「全局臨時表」和「觸發器」的概念只是不點擊。表是永久性數據存儲,它們的屬性也一樣 - 包括觸發器。臨時表在服務器重新啓動時被丟棄。爲什麼會有人設計了一個系統,其中的代碼(觸發)的永久塊取決於臨時共享存儲機制?這似乎是失敗的祕訣。

而不是全局臨時表,使用真正的表。如果你喜歡,在名字前加一個有用的前綴,如temp_。如果該表由數據庫共享,則將其放入數據庫中所有代碼均可訪問的數據庫中。

創建表一次,不管它(刪除行是罰款),所以觸發器代碼可以訪問它。

+0

感謝您的快速回答!雖然我承認我沒有考慮過永久代碼和臨時共享存儲概念,並且您給了我一些想法,但我確實有理由選擇使用全局臨時表: - 在我的工作中,我可以自由使用臨時表,但創建真正的表必須經歷一個不同的,正式的官僚過程;我儘量避免這種情況。 - 通過使用臨時表,我得到了SQL Server在我沒有會話使用它的情況下將其刪除的優勢。真正的桌子只會留在那裏並繼續增長。 –

+0

@loadH。 。 。哦,唉,官僚作風和奇怪的扭曲,讓事情真正完成。我完全理解。即便如此,也許有可能在一些啓動操作中創建全局臨時表,因此它可用於觸發器。我不喜歡嘗試在觸發器中創建表的想法。 –