我一直想知道 sp 中的临时表以及所有这些如何影响并发性。 SP 在 MSSQL 08 服务器上制作。
如果我有一个 SP,我在其中创建一个临时表并像这样再次删除它:
BEGIN
CREATE TABLE #MyTempTable
(
someField int,
someFieldMore nvarchar(50)
)
... Use of temp table here
... And then..
DROP TABLE #MyTempTable
END
这个 SP 会被非常频繁地调用,所以我的问题是这里会出现并发问题吗?
不。每个连接都会创建临时表的独立实例。
也许吧。
以 # (#example) 为前缀的临时表按会话保存。因此,如果您的代码在另一个调用正在运行时再次调用存储过程(例如后台线程),则创建调用将失败,因为它已经存在。
如果您真的担心,请改用表变量
DECLARE @MyTempTable TABLE
(
someField int,
someFieldMore nvarchar(50)
)
这将特定于该存储过程调用的“实例”。
其实不是,我说的是 SQL Server。临时表(带有单个 #)存在并且在其创建的范围内可见(范围限制)。每次调用存储过程时,它都会创建一个新范围,因此该临时表仅存在于该范围内。我相信临时表对于在该范围内调用的存储过程和 udf 也是可见的。但是,如果您使用双井号(##),那么它们在您的会话中成为全局的,因此作为创建临时表的会话的一部分对其他执行进程可见,您将不得不考虑临时表是否被访问的可能性同时是否可取。
根据 SQL Server 2008 书籍 您可以创建本地和全局临时表。本地临时表仅在当前会话中可见,全局临时表对所有会话可见。
'#table_temporal
'##table_global
如果在可由多个用户同时执行的存储过程或应用程序中创建本地临时表,数据库引擎必须能够区分不同用户创建的表。数据库引擎通过在内部将数字后缀附加到每个本地临时表名称来实现此目的。
那就没有问题了。
对于所有建议使用表变量的人,请谨慎这样做。表变量无法建立索引,而临时表可以建立索引。当处理少量数据时,表变量是最好的,但如果您处理较大的数据集(例如 50k 条记录),临时表将比表变量快得多。
还要记住,您不能依靠 try/catch 来强制执行存储过程中的清理操作。某些类型的失败无法在 try/catch 中捕获(例如,由于名称解析延迟而导致编译失败),如果您想真正确定,您可能需要创建一个包装存储过程来对工作存储过程进行 try/catch并在那里进行清理工作。
例如 创建 proc 工作进程 AS 开始 -- 在这里做点什么 结束
create proc wrapper AS
BEGIN
Create table #...
BEGIN TRY
exec worker
exec worker2 -- using same temp table
-- etc
END TRY
BEGIN CATCH
-- handle transaction cleanup here
drop table #...
END CATCH
END
表变量总是有用的一个地方是,当事务回滚时,它们“不会”回滚。这对于捕获您想要在主事务之外提交的调试数据非常有用...
: 根据官方文档:
https://learn.microsoft.com/en-us/sql/t-sql/statements/create-table-transact-sql?view=sql-server-ver15如果在存储过程中创建了本地临时表或者 可以由多个用户同时执行的应用程序, 数据库引擎必须能够区分由 不同的用户。数据库引擎通过内部执行此操作 将数字后缀附加到每个本地临时表名称。这 存储在 sysobjects 表中的临时表的全名 tempdb 由 CREATE TABLE 中指定的表名组成 语句和系统生成的数字后缀。为了允许 后缀,为本地临时名称指定的table_name不能超过 116 个字符。
如果您需要从下级存储过程访问数据(对于存储过程调用链来说这是一个邪恶的全局变量)并且没有其他干净的方法在存储过程之间传递数据,请使用#temp 表。 如果您需要二级索引,也可以使用它(不过,如果您需要多个索引,请真正问问自己它是否是#temp 表)
如果您这样做,
始终在函数顶部声明您的#temp 表。 当 SQL 看到 create table 语句时,它会强制重新编译你的存储过程......所以如果你在存储过程中间有 #temp 表声明,你的存储过程必须停止处理并重新编译。