我需要优化我的第一个 T-SQL 更新触发器

Posted

技术标签:

【中文标题】我需要优化我的第一个 T-SQL 更新触发器【英文标题】:I need to optimize my first T-SQL update trigger 【发布时间】:2015-01-07 21:12:39 【问题描述】:

如何在不使用大量变量的情况下重写此更新触发器?

我编写了我的第一个 SQL Server 触发器,它工作正常,但我认为,必须有一个更简单的解决方案。

如果 5 列中的至少一个被更改,我会在另一个表中写入两个新行。 第 1 行 = old Fahrer (=Driver) 和 old dispodate 和 update-time 第 2 行 = 新的 Fahrer 和新的 dispodate 和 updatedatetime 我的解决方案只是一个foxpro-trigger的副本,但是在T-SQL中必须有一个更简单的解决方案来检查一个列是否被更改。

ALTER TRIGGER [dbo].[MyTrigger]
ON [dbo].[tbldisposaetze]
AFTER UPDATE
AS
SET NOCOUNT ON;
/*SET XACT_ABORT ON 
SET ARITHABORT ON 
*/
DECLARE @oldfahrer varchar(10)
DECLARE @oldbus varchar(10)
DECLARE @olddispodat date
DECLARE @oldvzeit decimal(4,0)
DECLARE @oldbzeit  decimal(4,0)
DECLARE @oldbeschreibk varchar(255)

DECLARE @newfahrer varchar(10)
DECLARE @newbus varchar(10)
DECLARE @newdispodat date
DECLARE @newvzeit decimal(4,0)
DECLARE @newbzeit  decimal(4,0)
DECLARE @newbeschreibk varchar(255)

    SELECT  @oldfahrer = fahrer,@oldbeschreibk=beschreibk,@oldbus=bus,@oldbzeit=bzeit,@olddispodat=dispodat,@oldvzeit=vzeit
        FROM DELETED D
    SELECT  @newfahrer = fahrer,@newbeschreibk=beschreibk,@newbus=bus,@newbzeit=bzeit,@newdispodat=dispodat,@newvzeit=vzeit
        FROM inserted I

if @oldbeschreibk <> @newbeschreibk or @oldbus <> @newbus or @oldbzeit <> @newbzeit or @oldfahrer <> @newfahrer or @oldvzeit <> @newvzeit 
begin
  IF (SELECT COUNT(*) FROM tbldispofahrer where fahrer=@oldfahrer and dispodat=@olddispodat) > 0 
    update tbldispofahrer  set laenderung = GETDATE()  where fahrer=@oldfahrer and dispodat=@olddispodat
  else
    INSERT into tbldispofahrer (fahrer,dispodat,laenderung) VALUES (@oldfahrer,@olddispodat,getdate())

  IF (SELECT COUNT(*) FROM tbldispofahrer where fahrer=@newfahrer and dispodat=@newdispodat) > 0 
    update tbldispofahrer  set laenderung = GETDATE()  where fahrer=@newfahrer and dispodat=@newdispodat
  else
    INSERT into tbldispofahrer (fahrer,dispodat,laenderung) VALUES (@newfahrer,@newdispodat,getdate())
 end 

【问题讨论】:

好的,首先你不应该使用这样的变量!永远在任何触发器。删除和插入的表可以包含多条记录,您必须编写代码来考虑这一点。 您的触发器有 MAJOR 缺陷,因为您似乎认为它会每行调用一次 - 那是不是 b> 情况。触发器将每个语句触发一次,因此如果您的UPDATE 语句影响了 25 行,您将触发触发器一次,然后Inserted 和@987654324 @ 每个将包含 25 行。您的SELECT 代码将选择这 25 行中的哪一行?它是不确定的。您需要重写触发器以考虑到这一点! tbldispofahrer 表中的主键列是什么。 【参考方案1】:

我假设您拥有 SQL Server 2008 或更高版本。您可以在一个语句中完成所有这些操作,无需任何变量。

您可以在where 子句中轻松完成所有工作,而不是首先获取变量并查看它们是否不匹配。正如人们在 cmets 中所说,您可以将多行作为插入和删除的一部分。为了确保您使用的是相同的更新行,您需要通过主键进行匹配。

为了插入或更新行,我使用了MERGE 语句。合并的来源是带有上面 where 子句的联合,联合中的顶部表具有较旧的 fahrer,底部具有新的 farher。就像您的内部 IF 一样,现有行在 farher 和 dispodat 上匹配,并适当地插入或更新。

我注意到的一件事是,在您的示例中 newfahrer 和 oldfahrer 可能完全相同,因此应该只发生一次插入或更新(即,如果只有 bzeit 不同)。联合应防止尝试插入重复数据。我相信如果有合并会出错。

MERGE tbldispofahrer AS tgt
USING (
    SELECT d.farher, d.dispodat, GETDATE() [laenderung]
    INNER JOIN inserted i ON i.PrimaryKey = d.PrimaryKey
        AND (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreik ... )
    UNION
    SELECT i.farher, i.dispodat, GETDATE() [laenderung]
    INNER JOIN inserted i ON i.PrimaryKey = d.PrimaryKey
        AND (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreik ... )
) AS src (farher, dispodat, laenderung)
ON tgt.farher = src.farher AND tgt.dispodat = src.dispodat
WHEN MATCHED THEN UPDATE SET
    laenderung = GETDATE()
WHEN NOT MATCHED THEN
    INSERT (fahrer,dispodat,laenderung)
    VALUES (src.fahrer, src.dispodat, src.laenderung)

【讨论】:

【参考方案2】:

丹尼尔的回答中有一些小的语法错误。 以下代码运行良好:

MERGE tbldispofahrer AS tgt
USING (
    SELECT d.fahrer, d.dispodat, GETDATE() [laenderung] from deleted d
    INNER JOIN inserted i ON i.satznr = d.satznr
        AND (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreibk or i.bus <> d.bus or i.bzeit <> d.bzeit or i.vzeit <> d.vzeit)
    UNION
    SELECT i.fahrer, i.dispodat, GETDATE() [laenderung] from inserted i
    INNER JOIN deleted d ON i.satznr = d.satznr
        AND  (i.fahrer <> d.fahrer OR i.beschreibk <> d.beschreibk or i.bus <> d.bus or i.bzeit <> d.bzeit or i.vzeit <> d.vzeit)
) AS src (fahrer, dispodat, laenderung)
ON tgt.fahrer = src.fahrer AND tgt.dispodat = src.dispodat
WHEN MATCHED THEN UPDATE SET
    laenderung = GETDATE()
WHEN NOT MATCHED THEN
    INSERT (fahrer,dispodat,laenderung)
    VALUES (src.fahrer, src.dispodat, src.laenderung);

【讨论】:

以上是关于我需要优化我的第一个 T-SQL 更新触发器的主要内容,如果未能解决你的问题,请参考以下文章

t-sql如何创建连接2台服务器的触发器

尝试创建我的第一个 oracle 触发器时出现问题

SQL Server T—SQL 存储过程 触发器

列值更改的 T-SQL (SQL Server 2016) 触发器,在插入

在更新前使用触发器将一行插入到我的第二个表中

创建SQL触发器以反转先前的触发器