SQL Server 在插入时使用触发器删除行

Posted

技术标签:

【中文标题】SQL Server 在插入时使用触发器删除行【英文标题】:SQL Server delete rows with trigger on insert 【发布时间】:2020-09-12 14:04:12 【问题描述】:

我有两张桌子,

evaluer

id_ch 整数 | id_abonne int 外键引用 abonne|评估int |

abonne

id int primary key | name 

我必须在插入时向evaluer 表添加一个触发器。如果该 idabonne 的记录超过 5 条,则触发器应停止插入。我可以编写用于逐行插入的代码,但我希望它也适用于多行,而且我不想使用游标。我使用了 join 但它不起作用

create trigger eval
on evaluer
instead of insert
as
begin
    insert into evaluer(id_ch, id_abonne, evaluation)
        select * 
        from inserted i
        join (select ev.id_abonne
              from evaluer ev
              join inserted ins on ins.id_abonne = ev.id_abonne
              group by ev.id_abonne
              having count(*) <= 5) tc on i.id_abonne = tc.id_abonne
end

有没有不用光标的方法?

【问题讨论】:

因此,如果应用程序插入超过 5 行并且您的 只是忽略了第五行之后的所有内容,那么您认为在不向应用程序提供任何反馈的情况下简单地删除“附加”是否明智? ?还是“停止插入”实际上意味着应该引发错误,然后您的应用程序可以以某种方式响应? 我必须同意@SMor,您的数据库服务器的“静默”故障通常是一个坏主意,以后往往会导致很多问题。例如,inserted 伪表没有隐式顺序,因此如果插入超过五行,则无法保证实际插入哪些行。 我想在停止插入后显示一条消息,触发器应该插入前 5 条记录,然后停止添加更多 【参考方案1】:

类似的东西

create trigger eval
on evaluer
instead of insert
as
begin
if (select count(*) from inserted)<6
    insert into evaluer(id_ch,id_abonne,evaluation)
    select * from inserted;
end

【讨论】:

【参考方案2】:

所以我有一个不同的方法,5 是一个任意数字,当有人决定该数字应该更改时更改触发器可能会很麻烦。我建议创建一个名为EvaluerLimit 的第三个表,它只是一个身份/主键,然后插入其中 5 次。然后在Evaluer 中创建一个NOT NULL FOREIGN KEY,您的INSTEAD OF INSERT TRIGGER 将管理该ID。这将限制数据驱动,当发生错误时,未来的维护人员/编码人员将能够更容易地找出问题。

我还要注意,你提到删除旧记录,你有一个数据库,在当今世界你不应该删除记录,你想删除它们不是因为你只想要五条记录,如果那是在这种情况下,您应该将您的选择查询更改为前 5 个(可能按插入日期降序排列),而不是创建触发器。

以下是我如何设置限制,这是fiddle,您可以看到它的实际应用

 CREATE TABLE EvaluerLimit(
   Id TINYINT IDENTITY(1,1) PRIMARY KEY
 );
  
 CREATE TABLE Evaluer(
   Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
   AbonneId INT NOT NULL
     CONSTRAINT FK_Evaluer_Abonne_Id
     FOREIGN KEY REFERENCES Abonne(Id),
   EvaluerLimitId TINYINT NOT NULL
     CONSTRAINT FK_Evaluer_EvaluerLimit_Id
     FOREIGN KEY REFERENCES EvaluerLimit(Id),
     CONSTRAINT UQ_Evaluer_AbonneId_EvaluerLimitId
     UNIQUE(AbonneId, EvaluerLimitId)
);
 
DECLARE @i TINYINT = 0
WHILE @i < 5 BEGIN 
   INSERT INTO EvaluerLimit DEFAULT VALUES
   SET @i = @i + 1
END;

CREATE TRIGGER TR_EvaluerInsert ON Evaluer
INSTEAD OF INSERT
AS BEGIN
   DECLARE @limit TINYINT 
   SELECT @limit = ISNULL(MAX(e.EvaluerLimitId),0)+1 
   FROM Evaluer e
   INNER JOIN inserted 
     ON e.AbonneId = inserted.AbonneId
   
   INSERT INTO Evaluer(AbonneId, EvaluerLimitId)
   SELECT inserted.AbonneId, @limit
   FROM inserted
END;

【讨论】:

【参考方案3】:

虽然我重申了我在上面的 cmets 中提出的问题,但如果我必须这样做,我可能会使用这样的东西:

create trigger eval
on evaluer
instead of insert
as
begin
    insert into evaluer(id_ch, id_abonne, evaluation)
        select i.*
        from (select i.*, 
                row_number() over(partition by i.id_abonne order by (select null)) as iRowCount
              from inserted i) i
        join (select ev.id_abonne, count(id_abonne) as evCount
              from evaluer ev
              group by ev.id_abonne) ev  ON  ev.id_abonne = i.id_abonne
                                   And evCount < 5
                                   And iRowCount <= 5 - evCount

end

这会在inserted 中添加一个列,该列保持每个id_abonne 的新行的运行计数,并且还汇总了来自evaluer 的(group by) 表,其中包含已经存在的行的总数每个 id_abonne。然后它将它们加入到id_abonne 上,过滤掉任何会使 id_abonne 行数超过 5 的 inserted 行,并将剩余的内容插入表中。

【讨论】:

以上是关于SQL Server 在插入时使用触发器删除行的主要内容,如果未能解决你的问题,请参考以下文章

Sql Server 2005 - 插入更新触发器 - 获取更新,插入行

如何在 SQL Server 触发器中复制插入、更新、删除的行

SQL Server在复制时触发

SQL Server 触发器切换插入、删除、更新

插入后使用 SQL Server 触发器更新另一个表中的列

sqlserver2005触发器问题