我写了一个存储过程来导入数据并将数据从一个数据库转换到另一个数据库。每次导入都会获取一个公司ID,并导入与该公司相关的所有数据。
为了帮助转换步骤,我使用临时表。作为脚本审查的一部分,我被告知使用表变量而不是临时表。 评论者声称,如果我们同时运行两个不同的导入,临时表将被共享并破坏导入。
问题:
EXEC
是否都会创建新范围?这是一个人为的脚本示例。
CREATE PROC [dbo].[ImportCompany]
(
@CompanyId AS INTEGER
)
AS
EXEC [dbo].[ImportAddress] @CompanyId = @CompanyId
--Import other data
CREATE PROC [dbo].[ImportAddress]
(
@CompanyId AS INTEGER
)
AS
CREATE TABLE #Companies (OldAddress NVARCHAR(128), NewAddress NVARCHAR(128))
INSERT INTO #Companies(OldAddress, NewAddress)
SELECT
Address as OldAddress,
'Transformed ' + Address as NewAddress
FROM
[OldDb].[dbo].[Addresses]
WHERE
CompanyId = @CompanyId
--Do stuff with the transformed data
DROP TABLE #Companies
EXEC [dbo].[ImportCompany] @CompanyId = 12345
答案 0 :(得分:52)
来自CREATE TABLE
:
本地临时表仅在当前会话中可见
和(更重要的):
如果在可由多个用户同时执行的存储过程或应用程序中创建本地临时表,则数据库引擎必须能够区分由不同用户创建的表 [sic - 几乎当然这应该说会话不是用户] 。数据库引擎通过在内部为每个本地临时表名附加数字后缀来完成此操作。
这恰恰反驳了谁说他们将被分享的观点。
此外,您的程序结束时无需DROP TABLE
(再次来自相同的链接):
存储过程完成时,将自动删除在存储过程中创建的本地临时表
答案 1 :(得分:32)
##
用于全局临时表 - 可用于不同的导入。
#
用于本地临时表,仅在当前/内部范围内可用。
答案 2 :(得分:19)
一个会话无法看到另一个会话的临时表。因此,无论您使用临时表还是表变量,不同的导入都不会相互影响。
例外是全局临时表,以##
开头。所有人都可以看到这些。
答案 3 :(得分:3)
我花了几个小时努力找出为什么触发器中使用的临时表行为奇怪。然后我意识到临时表与用于插入触发触发器的数据的存储过程中的临时表具有相同的名称。 我现在意识到这对我来说应该是显而易见的,但这是一个典型的例子,当试图找出为什么没有意义时,忽略了最明显的原因。
因此,重要的是要记住,当存储的proc调用另一个存储过程或触发一个触发器时,临时表名称必须在这些名称中是唯一的,以防止不必要的副作用。
此外 - 即使在内部存储过程中执行以下代码时,它也无法按预期运行。由于外部存储过程似乎锁定临时表名。
IF OBJECT_ID('tempdb..#TempTable') IS NOT NULL
DROP TABLE #TempTable