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。 。 。哦,唉,官僚作风和奇怪的扭曲,让事情真正完成。我完全理解。即便如此,也许有可能在一些启动操作中创建全局临时表,因此它可用于触发器。我不喜欢尝试在触发器中创建表的想法。 –