我在 MSSQL Server 2008R2 中有一个触发器:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER TRIGGER [dbo].[trg_HosFile_Delete]
ON [dbo].[hosfile] FOR DELETE
AS
insert into #pys(pyGuid)
SELECT EntryGuid AS pyGuid FROM er000 AS er
insert into t2(C1) select pyGuid from #pys
触发器执行后,
t2
表为空。为什么是空的?
如果我在没有触发器的情况下执行上面的查询,
t2
表将被填充。
在触发器中使用临时表有什么问题吗?
Damien 的答案是正确的:您可以在触发器中使用临时表,但强烈建议在那里定义它们,因为触发器可以在各种上下文中触发。
如果您在触发器中使用临时表,请检查临时表是否存在,因为您无法控制上下文,它可能已经存在:
IF OBJECT_ID('tempdb..#pys') IS NOT NULL
DROP TABLE #pys
此外,可以动态创建临时表:
SELECT * INTO #tmp
FROM inserted
当触发器包含需要访问在动态 SQL 范围中不可见的插入或删除的特殊表的动态 SQL 时,这特别有用。
避免使用 ##tmp(全局临时变量),因为它们是全局可见的,并且当多个 SPID 触发您的触发器时可能会导致麻烦。
使用临时表没有问题,只要它们在触发器触发时位于范围内。
鉴于触发器可以在任何连接上随时触发,唯一有意义的范围是触发器体内:
ALTER TRIGGER [dbo].[trg_HosFile_Delete]
ON [dbo].[hosfile] FOR DELETE
AS
CREATE TABLE #pys (pyGuid uniqueidentifier not null/*I'm guessing*/)
insert into #pys(pyGuid)
SELECT EntryGuid AS pyGuid FROM er000 AS er
insert into t2(C1) select pyGuid from #pys
(说实话,我不确定您是否可以从外部作用域访问临时表,并且没有方便的实例来测试它。但是,即使可以,它也会导致非常脆弱的触发器)
如果您使用##pys(注意双#号),它将在创建后全局可用。这可能对你的情况有帮助。
尝试使用 SELECT INTO 创建并填充临时表。在触发器内使用临时表时存在一些限制。
只是对此线程的快速补充,因为从其他帖子中不清楚触发器和发起查询之间临时表的连续性:
可以在触发器内读取(SELECT FROM)和更新(INSERT、UPDATE 或 DELETE)会话 (#) 临时表。
我发现这对于将数据传递到可能需要完成的触发器特别有用。
例如,我在触发器中使用它,将表和字段级别的更改写入审计日志文件,该文件的内容是“用户 X 将字段 Y 从 A 更改为 B”。但为了实现这一点,我需要有关数据库用户的信息(他不是运行服务的实际 SQL Server 用户,而是我的“用户”表中的用户。)
将此信息传递给触发器的唯一方法是使用填充有用户凭据和其他会话信息的#Table。始终测试触发器内临时表是否存在。
触发前:
IF object_id(''tempdb..#UserCredentials'') IS NOT NULL');
EXEC('DROP TABLE #UserCredentials')
CREATE TABLE #UserCredentials ([UserType] VARCHAR(20), [UserID] BIGINT, [Date] DATETIME)
:
DELETE FROM #UserCredentials
INSERT INTO #UserCredentials ([UserType], [UserID], [Date])
VALUES (@system_usertype, @system_userid, @system_date)
UPDATE MyTable...
触发器内部:
IF object_id('tempdb..#UserCredentials') IS NOT NULL
SELECT @system_usertype = [UserType], @system_userid = [UserID], @system_date = [Date] FROM #UserCredentials