【问题标题】:SQL-Server Trigger Returning Null Values For DELETED Row Field ValuesSQL-Server 触发器为 DELETED 行字段值返回 Null 值
【发布时间】:2019-06-18 15:17:27
【问题描述】:

我遇到了一个问题,我需要在进行更新之前将记录的副本保存到“历史”表中,其中包含更新记录的原始字段值。

我正在使用触发器来执行此操作。触发器 INSERTED 值应包含更新后的行字段值,而 DELETED 应显示更新前的字段值。

但是,每当示例触发器代码运行时,SELECT 的返回值始终为 ArticleID、Title、StepContent 和 SortOrder 参数返回 null。但是,如果我要更改从 DELETED 中提取的代码行以从 INSERTED 中提取,它就可以正常工作。

ALTER TRIGGER [dbo].[TRG_SaveHistory]
ON [dbo].[ArticleStep]
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;


DECLARE @SortOrder INT, 
        @ArticleID INT, 
        @ArticleHistoryID INT, 
        @Title varchar(max), 
        @StepContent varchar(max);

--SELECT @SortOrder = SortOrder, @Title = Title, @StepContent = StepContent, @ArticleID = ArticleID FROM INSERTED;
SELECT TOP (1) @SortOrder = SortOrder, @Title = Title, @StepContent = StepContent, @ArticleID = ArticleID FROM DELETED WHERE @SortOrder != null;
SELECT TOP (1) @ArticleHistoryID = ID FROM dbo.ArticleHistory WHERE OriginalArticleID = @ArticleID ORDER BY ID DESC;

IF  @SortOrder IS NOT Null AND @ArticleHistoryID IS NOT NULL
    INSERT INTO [dbo].[ArticleStepHistory]
               ([ArticleHistoryID]
               ,[SortOrder]
               ,[Title]
               ,[StepContent])
         VALUES
               (@ArticleHistoryID
               ,@SortOrder
               ,@Title
               ,@StepContent);
END

当我改变这个时工作:

SELECT @SortOrder = SortOrder, @Title = Title, @StepContent = StepContent, @ArticleID = ArticleID FROM DELETED;

对此:

SELECT @SortOrder = SortOrder, @Title = Title, @StepContent = StepContent, @ArticleID = ArticleID FROM INSERTED;

这是我正在运行的更新查询:

UPDATE [dbo].[ArticleStep] SET [ArticleID] = 2 ,[SortOrder] = 3,[StepContent] = 'Changed Text' WHERE ID = 1

【问题讨论】:

  • 如果您假设inserteddeleted 只有一行,那么您在触发器中犯了一个致命错误。你应该问另一个关于如何在 SQL Server 中正确编写触发器的问题。
  • 请记住,删除和插入的行集可能不止一行。请务必将它们视为集合。
  • 顺便说一句,从 2016 版本开始,您可以使用系统版本表,让 SQL Server 为您完成繁重的工作,而不是为此编写触发器。
  • 一边;使用sp_settriggerorder 将触发顺序设置为'Last' 是一个好主意。如果另一个触发器导致rollback,那么您还没有记录update
  • @matwonk 并非没有更新语句的示例,它更新的行(之前和之后),以及对目标的更好定义。讨论已知存在重大故障的触发器的逻辑确实没有意义 - 即单行假设。此外,您的逻辑取决于历史表中是否存在关联的行,以便将“有效”值插入同一个表中。我认为是时候重新开始并重新考虑你的整个方法了。至少,需要更多信息。

标签: sql sql-server database tsql database-trigger


【解决方案1】:

所以这里是你的触发器可能是什么样子的疯狂猜测:

alter trigger dbo.TRG_SaveHistory
   on dbo.ArticleStep after update 
as
begin 
set nocount on;

if not exists (select * from deleted) 
    return;

with cte as (
  select max(ID) as HistoryID, OriginalArticleID 
    from dbo.ArticleHistory 
   group by OriginalArticleID)
insert dbo.ArticleStepHistory (ArticleHistoryID, SortOrder, Title, StepContent)
select cte.HistoryID, deleted.SortOrder, deleted.Title, deleted.StepContent
from deleted inner join cte 
  on deleted.ArticleID = cte.OriginalArticleID; 

end;

这是一个完整的猜测。您可能需要针对轻微的语法错误对其进行调整,因为我没有您的数据库并使用记事本编写。如果您正在编写 tsql 代码,您应该能够(并且预计能够)纠正小错误。

了解 ArticleStep 的主键、历史记录表的主键和外键(即使未强制执行 FK)以及所有这些行应该如何与其原始值相关联以及可能与其他行相关联非常重要“更高级别”的行。您需要从历史表中检索 ID 以便向其中添加行,这有点奇怪。这意味着您有(或应该有)一个插入触发器。如果你这样做,它可能也会受到相同的单行假设的影响。

但实际上不必为了插入更多行而从历史表中“查找”一个 id。在其他日志记录情况下,我还没有看到这种需求。您应该仔细考虑为什么您认为需要这样做。

标题是 varchar(max)?我也会重新考虑。也许这会给你推动一个更好的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多