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 - 插入更新触发器 - 获取更新,插入行