为什么我的SQL Server审核触发器搞乱了来自Access的ODBC调用/刷新?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么我的SQL Server审核触发器搞乱了来自Access的ODBC调用/刷新?相关的知识,希望对你有一定的参考价值。

我在其中一个表上实现了一个审计触发器,它基本上将旧记录和新记录复制到一个名为... _ Audit的表中,以及日期和用户。我会进一步发布我的脚本。

问题是,当我在Access中插入新记录然后选项卡时,它会刷新并显示表中的第一条记录。下面是一个例子 - 我已经添加了前三个记录然后刷新,然后我添加了三个完全相同的数据添加它们之后我应该看到具有相同数据和增加ID的记录,但是他们已经抓住了第一个最后三条记录的表的三条记录。

 P_SubtaskID  PresetID_FK P_SubtaskName            DateDay DateMonth
 148          17          a new subtask            1       7
 149          17          a new subtask            1       7
 150          17          a new subtask            1       7
 8            5           Receive, sign and save   25      10
 9            5           Electronic lodgement     30      10
 10           1           Review                   12      7

点击刷新后,这些记录会显示它们应该显示的内容:

 P_SubtaskID  PresetID_FK P_SubtaskName            DateDay DateMonth
 148          17          a new subtask            1       7
 149          17          a new subtask            1       7
 150          17          a new subtask            1       7
 151          17          a new subtask            25      10
 152          17          a new subtask            30      10
 153          17          a new subtask            12      7

这是有问题的,因为当用户添加记录然后保存它时,它显示完全不相关的记录。如果他们对此幻影进行了更改,则会影响该记录 - 这很容易导致错误更新/删除记录等。不好!

所以这是我创建审计表及其相关触发器的脚本:

USE [ClientDatabase]
 GO

 SET ANSI_NULLS ON
 GO
 SET QUOTED_IDENTIFIER ON
 GO

 DROP TABLE [dbo].[AutoTaskPresets_Subtasks_Audit]
 GO

 CREATE TABLE [dbo].[AutoTaskPresets_Subtasks_Audit](
    SessionID int identity(1,1) not null,
    [P_SubtaskID] [int] NULL,
    [PresetID_FK] [int] NULL,
    [P_SubtaskName] [nvarchar](255) NULL,
    [DateDay] [int] NULL,
    [DateMonth] [int] NULL,
    [DatePeriod] [int] NULL,
    [StaffName_FK] [nvarchar](255) NOT NULL,
    Action nchar(10) null,
    RowType nchar(10) null,
    ChangedDate datetime not null default getdate(),
    ChangedBy sysname not null default user_name()
 )
 GO

 CREATE Trigger [dbo].[DeleteAutoTaskPresets_Subtasks] ON [dbo].
     [AutoTaskPresets_Subtasks] FOR DELETE AS  

 BEGIN  
     SET NOCOUNT ON
     INSERT dbo.AutoTaskPresets_Subtasks_Audit(
        [P_SubtaskID],
        [PresetID_FK],
        [P_SubtaskName],
        [DateDay],
        [DateMonth],
        [DatePeriod],
        [StaffName_FK],
        Action,
        RowType)
     SELECT
        [P_SubtaskID],
        [PresetID_FK],
        [P_SubtaskName],
        [DateDay],
        [DateMonth],
        [DatePeriod],
        [StaffName_FK],
        'Deleted',
        'Old'
    FROM Deleted
 END
 GO

 CREATE Trigger [dbo].[InsertAutoTaskPresets_Subtasks]
    ON [dbo].[AutoTaskPresets_Subtasks] FOR INSERT AS  
 BEGIN  
     SET NOCOUNT ON
     INSERT dbo.AutoTaskPresets_Subtasks_Audit(
        [P_SubtaskID],
        [PresetID_FK],
        [P_SubtaskName],
    [DateDay],
        [DateMonth],
        [DatePeriod],
        [StaffName_FK],
        Action,
        RowType)
     SELECT
        [P_SubtaskID],
        [PresetID_FK],
        [P_SubtaskName],
        [DateDay],
        [DateMonth],
        [DatePeriod],
        [StaffName_FK],
        'Inserted',
        'New'
    FROM Inserted

 END
 GO

 CREATE Trigger [dbo].[UpdateAutoTaskPresets_Subtasks]
ON [dbo].[AutoTaskPresets_Subtasks] FOR UPDATE AS  
 BEGIN  
     SET NOCOUNT ON
     INSERT dbo.AutoTaskPresets_Subtasks_Audit(
        [P_SubtaskID],
    [PresetID_FK],
    [P_SubtaskName],
    [DateDay],
    [DateMonth],
    [DatePeriod],
    [StaffName_FK],
    [Action],
    RowType)
     SELECT
    [P_SubtaskID],
    [PresetID_FK],
    [P_SubtaskName],
    [DateDay],
    [DateMonth],
    [DatePeriod],
    [StaffName_FK],
    'Updated',
    'Old'
 FROM Deleted

     INSERT dbo.AutoTAskPresets_Subtasks_Audit(
    [P_SubtaskID],
    [PresetID_FK],
    [P_SubtaskName],
    [DateDay],
    [DateMonth],
    [DatePeriod],
    [StaffName_FK],
    [Action],
    RowType)
     SELECT
    [P_SubtaskID],
    [PresetID_FK],
    [P_SubtaskName],
    [DateDay],
    [DateMonth],
    [DatePeriod],
    [StaffName_FK],
    'Updated',
    'New'
FROM Inserted

 END
 GO

我很难过为什么会这样。我所能想到的可能只是一个时间问题,虽然目前触发器在我认为是优先选择的行动之后运行。

我确信这是我的触发器,因为当我删除它们时,它工作正常。我的数据库也从未显示过这样的错误。

我正在使用SQL Server 2005与MS Access 2007客户端的ODBC连接。

答案

正如Nikola所解释的那样,该问题与@@ identity的使用有关。这将获取最后一个记录条目的标识值,因此当我执行插入时,触发器会插入到另一个表中。这导致了@@ identity的不同值,这是MS Access似乎错误依赖的。要解决这个问题,我需要在运行触发器之前保存@@ identity值,然后恢复它。如下所示,插入触发器:

 CREATE Trigger [dbo].[InsertAutoTaskPresets_Subtasks]
    ON [dbo].[AutoTaskPresets_Subtasks] FOR INSERT AS  
 BEGIN  
     SET NOCOUNT ON

     declare @id int
     set @id = @@identity

     INSERT dbo.AutoTaskPresets_Subtasks_Audit(
        [P_SubtaskID],
        [PresetID_FK],
        [P_SubtaskName],
    [DateDay],
        [DateMonth],
        [DatePeriod],
        [StaffName_FK],
        Action,
        RowType)
     SELECT
        [P_SubtaskID],
        [PresetID_FK],
        [P_SubtaskName],
        [DateDay],
        [DateMonth],
        [DatePeriod],
        [StaffName_FK],
        'Inserted',
        'New'
    FROM Inserted

DECLARE @SQL varchar(8000)
SET @sql = 'SELECT IDENTITY(INT, ' + CAST(@id as varchar) + ', 1)
         AS ident INTO #Tmp'
EXEC(@sql)

 END
 GO

令人遗憾的是,Access似乎是以这种方式构建的,依赖于不是100%准确的东西。

最后,我只在Insert触发器上实现了这一点,即使其他触发器使用插入操作。经过一些简短的测试后,这似乎没问题。我现在已经在我的所有表格上推出了这个脚本,可以由普通用户编辑,所以不久之后我就知道这是否会引起问题。

以上是关于为什么我的SQL Server审核触发器搞乱了来自Access的ODBC调用/刷新?的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server 触发器触发 3 次

SQL Server表审核触发器

SQL 触发器 - 有一个会搞乱新的 SQL 查询

删除单个表时 SQL Server 触发器

SQL Server 审核事件日志

从 SQL Server 触发器调用 Freshdesk API