【问题标题】:SQL Server : INSTEAD OF UPDATE trigger: dynamic code generation; inserted table not availableSQL Server:INSTEAD OF UPDATE 触发器:动态代码生成;插入的表不可用
【发布时间】:2019-03-16 21:21:13
【问题描述】:

我有一个INSTEAD OF UPDATE 触发器,它动态生成代码然后执行它。问题是当代码执行时我总是得到错误信息:

无效的对象名称“插入”

触发代码是这样的:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER Trigger [dbo].[TACMasterLayouts_VersionVisibilityHandling_updates] 
ON [dbo].[TACMasterLayouts_VersionVisibilityHandling] 
INSTEAD OF UPDATE
AS
    IF @@rowcount = 0 
        RETURN; 

    SET NOCOUNT ON

    DECLARE @tableToUpdate AS NVARCHAR(MAX) = 'dbo.tacmasterlayouts';
    DECLARE @UpdateStatement AS NVARCHAR(MAX) 
    DECLARE @IdentityField AS NVARCHAR(MAX) = (SELECT Name 
                                               FROM sys.columns 
                                               WHERE [object_id] = OBJECT_ID('dbo.tacmasterlayouts') 
                                                  AND is_identity = 1)

    SET @UpdateStatement = 'update ' + @tableToUpdate + ' set ';

    DECLARE @Fields AS NVARCHAR(MAX)

    SELECT @Fields = COALESCE(@Fields + ', ', '') + @tableToUpdate + '.' + Name + ' = inserted.' + Name 
    FROM sys.columns 
    WHERE [object_id] = OBJECT_ID('dbo.tacmasterlayouts') 
      AND Name != @IdentityField;

    SET @UpdateStatement = @UpdateStatement + @fields

    SET @UpdateStatement = @UpdateStatement + ' FROM ' + @tableToUpdate + ', inserted'
    SET @UpdateStatement = @UpdateStatement + ' where ' + @tableToUpdate + '.' + @IdentityField + ' = inserted.' + @IdentityField

    EXECUTE sp_executesql @UpdateStatement

动态代码如下:

UPDATE TACMasterLayouts SET

 TACMasterLayouts.TACMasterLayoutName = inserted.TACMasterLayoutName, 
 TACMasterLayouts.TACMasterLayoutDestinationFileName = inserted.TACMasterLayoutDestinationFileName, 
 TACMasterLayouts.LayoutTypeCategoryId = inserted.LayoutTypeCategoryId, 
 TACMasterLayouts.LayoutTypeId = inserted.LayoutTypeId, 
 TACMasterLayouts.TACMasterLayoutDescription = inserted.TACMasterLayoutDescription, 
 TACMasterLayouts.TACMasterLayoutJasper = inserted.TACMasterLayoutJasper, 
 TACMasterLayouts.TACMasterLayoutJrxml = inserted.TACMasterLayoutJrxml, 
 TACMasterLayouts.TACMasterLayoutHtml = inserted.TACMasterLayoutHtml, 
 TACMasterLayouts.TACMasterLayoutDontClone = inserted.TACMasterLayoutDontClone, 
 TACMasterLayouts.TACMasterLayoutIsSubReport = inserted.TACMasterLayoutIsSubReport, 
 TACMasterLayouts.TACMasterLayoutVersion = inserted.TACMasterLayoutVersion, 
 TACMasterLayouts.TACMasterLayoutKey = inserted.TACMasterLayoutKey, 
 TACMasterLayouts.TACMasterLayoutTimestampLastModified = inserted.TACMasterLayoutTimestampLastModified, 
 TACMasterLayouts.TACMasterLayoutHash = inserted.TACMasterLayoutHash, 
 TACMasterLayouts.TACMasterLayoutTimestampLastCheckout = inserted.TACMasterLayoutTimestampLastCheckout,
 TACMasterLayouts.TACMasterLayoutCheckedOutByUserId = inserted.TACMasterLayoutCheckedOutByUserId,
 TACMasterLayouts.TACMasterLayoutIsCheckedIn = inserted.TACMasterLayoutIsCheckedIn, 
 TACMasterLayouts.TACMasterLayoutCheckedInByUserId = inserted.TACMasterLayoutCheckedInByUserId, 
 TACMasterLayouts.TACMasterLayoutCheckedOutFolderName = inserted.TACMasterLayoutCheckedOutFolderName 

FROM TACMasterLayouts, inserted
WHERE TACMAsterlayouts.TACMasterLayoutId = inserted.TACMasterLayoutId;

提示:

如果我在触发器中静态输入动态生成的代码,它可以正常工作。所以代码本身没问题。

问题:为什么inserted 在这种情况下不可用?以及如何解决?

【问题讨论】:

  • 小心where d.TACMasterLayoutId = (select TACMasterLayoutId from inserted)它可能会返回很多行。
  • @sami:不——这不是同一个问题,因为当动态生成的代码在此触发器中静态输入时,它可以正常工作。
  • 一个更好的主意是使用动态sql来编写你的触发器而不是用它来执行你的触发器。

标签: sql-server dynamic triggers sqlcode


【解决方案1】:

根据我的发现,您似乎无法以这种方式在动态 SQL 中使用 inserted 表,但您应该能够使用SQLCLR

Using the inserted / deleted tables in Dynamic SQL

【讨论】:

  • 我在纯 SQL 中 - 没有 C#。
  • 对不起 - 不,我没有因为 C# 部分。是的 - 链接的文章还使用#xxx 将插入/删除传递给 sp_executesql。因此,我给你一个 +1 来取消投票给你的那个:-) 也许你可以编辑你的答案并删除 C# 部分来澄清你的意思。
【解决方案2】:

执行动态 sql 时,它有自己的范围,就像调用另一个存储过程一样,因此它不能引用触发器范围内的内容,例如插入、删除或局部变量等。您必须在所需键值的动态 sql 字符串,并准备处理多于 1 行的插入导致触发器触发。

【讨论】:

  • 因为我不是 SQL 专家(还没有!) - 是否可以将“插入”表传递给 sp_executesql?我必须动态生成 SQL 代码,因为表和视图可以随时更改字段的数量和名称——我有很多需要这个的表——而且我不想更新(运行)而不是触发器修改基础表/视图时的代码。
【解决方案3】:

由于上述答案没有给我想要的解决方案,我进一步挖掘并找到了以下 - 酷 - 解决方案(感谢以下文章:https://www.sqlteam.com/forums/topic.asp?TOPIC_ID=11318):

如果您需要将插入的表传递给动态 SQL 代码(传递给 sp_executesql 存储过程),请执行以下操作:

select * from inserted into #inserted

然后您可以立即在#inserted 下的sp_executesql 中访问它。无需将此作为参数传递给 sp_executesql。

最终的工作代码如下所示:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER Trigger [dbo].[TACMasterLayouts_VersionVisibilityHandling_updates] on [dbo].[TACMasterLayouts_VersionVisibilityHandling] INSTEAD OF update  
as 
 if @@rowcount = 0 return; 
 SET NOCOUNT ON

 Declare @tableToUpdate AS Nvarchar(max) = 'dbo.tacmasterlayouts';
 Declare @UpdateStatement AS Nvarchar(max)
 Declare @IdentityField as Nvarchar(max) = (SELECT Name FROM sys.columns WHERE [object_id] = OBJECT_ID(@tableToUpdate) and is_identity = 1)

 set @UpdateStatement = 'update ' + @tableToUpdate + ' set ';

 Declare @Fields AS Nvarchar(MAX)
 SELECT  @Fields = COALESCE(@Fields + ', ', '') + @tableToUpdate + '.' + Name + ' = #inserted.' + Name FROM sys.columns WHERE [object_id] = OBJECT_ID(@tableToUpdate) and Name != @IdentityField;

 set @UpdateStatement = @UpdateStatement + @fields

 set @UpdateStatement = @UpdateStatement + ' FROM ' + @tableToUpdate + ', #inserted'
 set @UpdateStatement = @UpdateStatement + ' where ' + @tableToUpdate + '.' + @IdentityField + ' = #inserted.' + @IdentityField

 select * into #inserted from inserted 

 EXECUTE sp_executesql @UpdateStatement

【讨论】:

    猜你喜欢
    • 2017-11-26
    • 2011-09-25
    • 2014-06-14
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多