【问题标题】:SQL Server delete rows with trigger on insertSQL Server 在插入时使用触发器删除行
【发布时间】: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 条记录,然后停止添加更多

标签: sql sql-server


【解决方案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 行,并将剩余的内容插入表中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-12-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-07
        • 1970-01-01
        • 1970-01-01
        • 2011-11-25
        相关资源
        最近更新 更多