无法使用MySQL触发器删除一行

huangapple go评论70阅读模式
英文:

Unable to delete a row using MySQL triggers

问题

我在MySQL中有一个触发器

 DELIMITER $$
CREATE TRIGGER trigger2
BEFORE INSERT ON participated FOR EACH ROW
BEGIN    
IF((SELECT COUNT(*) FROM participated WHERE driver_id = NEW.driver_id) > 3) THEN 
    DELETE FROM accident WHERE report_no = NEW.report_no;         
    SIGNAL SQLSTATE '45000' SET message_text = "Driver is already involved in 3 accciddents"; 
END IF; 
END;$$ 
DELIMITER ;

首先,在accident表中插入事故报告。在插入到participated表之前,如果涉及到参与了超过3起事故的驾驶员,需要发出警告,并且应删除accident表中驾驶员的数据。

'accident'和'participated'是两个表。
 
accident(report_no,date,location);
participated(driver_id,reg_no,report_no,amount);

例如:

insert into accident values(34,"2022-04-05","bangalore");

insert into participated values("D1","KA-09-MM-5644",34,20000);

错误1644 (45000): 驾驶员已经参与了3起事故

警告正常工作,但未删除accident表中的行。accident表仍然具有report_no 34的行。

英文:

I have a trigger in MySQL

 DELIMITER $$ 
CREATE TRIGGER trigger2
BEFORE INSERT ON participated FOR EACH ROW
 BEGIN    
 IF((SELECT COUNT(*) FROM participated WHERE driver_id = NEW.driver_id) > 3) THEN 
     DELETE FROM accident WHERE report_no = NEW.report_no;         
SIGNAL SQLSTATE '45000' SET message_text = "Driver is already involved in 3 accciddents"; 
END IF; 
END;$$ 
DELIMITER ;

First an accident report is inserted into accident table. Before inserting in to participated table if it involves a driver who has participated in more than 3 accident a waring has to be given and driver's data in accident table should be deleted.

'accident' and 'participated' are the two tables.

 accident(report_no,date,location);
  participated(driver_id,reg_no,report_no,amount);

ex:

 insert into accident values(34,"2022-04-05","bangalore");

 insert into participated values("D1","KA-09-MM-5644",34,20000);

 ERROR 1644 (45000): Driver is already involved in 3 accciddents

Warning is working but it is not deleting the row in accident table. accident table still has the row with report_no 34

答案1

得分: 2

MySQL触发器中的代码如单个ALL-OR-NOTHING事务。这意味着其中的每个查询都必须成功,否则整个过程将被撤销。通过使用SIGNAL SQLSTATE '45000' SET message_text,会故意引发错误,导致迄今为止发生的一切都被回滚,并返回一条消息。请注意,由于引发了错误,INSERT语句本身被取消。当然,可以在触发器的开头声明一个继续处理程序以忽略错误。

这将确保在遇到SQLSTATE '45000'后继续执行。然而,message_text是被忽略的,因为它只是用于显示警告/错误,而不是用于继续处理程序。遗憾的是,我们不能在触发器中使用SELECT语句或类似的内容返回结果集,所以如果我们在SIGNAL语句之后添加SELECT语句或类似的内容,会出现错误:

如果我们真的需要显示一条消息,可以考虑使用存储过程来绕过触发器强加的限制:

通过使用存储过程,我们可以显示一条消息。如果我们直接使用INSERT语句(当我们忘记使用存储过程时),触发器的操作仍然适用。因此,我们可以考虑添加INSERT语句以填充一个审计表以供将来参考。

英文:

The body part in Mysql trigger is like a single ALL-OR-NOTHING transaction. This means every query inside has to be successful, or the entire process is undone. By using SIGNAL SQLSTATE '45000' SET message_text , an error is intentionally raised, which rolls back every thing that has happend so far, and a message is returned. Note, the INSERT statement itself is cancelled due to error induced. Of course, it's possible to ignore the error by declaring a continue handler in the very begining of the trigger.

BEGIN
declare continue handler for SQLSTATE '45000' begin end;    
 IF((SELECT COUNT(*) FROM participated WHERE driver_id = NEW.driver_id) > 3) THEN 
     DELETE FROM accident WHERE report_no = NEW.report_no;         
SIGNAL SQLSTATE '45000' SET message_text = "Driver is already involved in 3 accciddents"; 
END IF; 
END  

This will make sure things keep going after SQLSTATE '45000' is encountered. However, the message_text is IGNORED, as it's only intended to show up to address a warning/error, not for a continue handler. And regrettably, we can not return a result set using a trigger. So if we add a SELECT statement or something alike after the SIGNAL statement , an error will pop up:

select "Driver is already involved in 3 accciddents" as a warning; 
-- OR
show warnings;
-- Error Code: 1415. Not allowed to return a result set from a trigger

If we really need a message to show up,we can considering using a procedure to bypass the restriction enforced by trigger:

DELIMITER $$ 
CREATE TRIGGER trigger2
BEFORE INSERT ON participated FOR EACH ROW
 BEGIN    
 IF((SELECT COUNT(*) FROM participated WHERE driver_id = NEW.driver_id) > 3) THEN 
     DELETE FROM accident WHERE report_no = NEW.report_no;         
SET @warning = "Driver is already involved in 3 accciddents"; -- here we don't really need a SIGNAL statement. Just creating a user variable is adequate.
else set @warning=null;
END IF; 
END$$

create procedure insert_participated (d_id varchar(20),rg_no varchar(20),rp_no int,amt int)
begin
insert into participated values(d_id,rg_no,rp_no,amt);
if @warning is not null then 
select @warning as warning;
end if;
end $$ 
DELIMITER ;

By using a procedure ,we can display a message. And if we directly use an insert statement(when we forget to use the procedure), the trigger's operation still applies. Therefore, we might think about adding an INSERT statement to populate an auditing table for future reference.

huangapple
  • 本文由 发表于 2023年2月10日 15:47:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/75408222.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定