触发优化

时间:2019-01-20 09:28:12

标签: mysql triggers mariadb

如何优化此触发器?效果很好,但我认为可以改进:

CREATE TRIGGER `update_request_missions_after_insert` BEFORE INSERT ON `request_missions`
 FOR EACH ROW
IF ((SELECT id FROM request_missions ORDER BY id DESC LIMIT 1)+1) >= 1  AND 
   ((SELECT id FROM request_missions ORDER BY id DESC LIMIT 1)+1) <= 9 THEN
        SET New.rm_number = CONCAT(DATE_FORMAT(CURDATE(),'%y'), '-',
            CONCAT('00000',(SELECT id FROM request_missions ORDER BY id DESC LIMIT 1)+1));
    END IF

结果将在rm_number列中,例如:

id> = 1和id <= 9

19-000001
19-000002
...
19-000009

2 个答案:

答案 0 :(得分:0)

这里建议使用局部变量,而不要重复三次子查询。你说那是你的担心。

CREATE TRIGGER `update_request_missions_after_insert` BEFORE INSERT ON `request_missions`
FOR EACH ROW
BEGIN
  DECLARE next_id INT;
  SELECT COALESCE(MAX(id),0) + 1 INTO next_id FROM request_missions;
  IF next_id BETWEEN 1 AND 9 THEN
    SET NEW.rm_number = CONCAT(DATE_FORMAT(CURDATE(),'%y'), '-', '00000', next_id);
  END IF;
END

警告:这会遇到比赛条件。例如,如果两个会话同时插入到表中,则它们可能都读取相同的next_id

同样,如@ P.Salmon所述,如果next_id为> 9,则结果不确定。我不确定您要做什么。

您可能希望允许更长的数字,并使用零填充它们到固定长度:

LPAD(next_id, 6, '0')

有关LPAD()功能的信息,请参见手册。

如果表有零行,我使用COALESCE(MAX(id),0)来确保至少返回一个非NULL值。

这也涉及到:

DATE_FORMAT(CURDATE(),'%y')

两位数的年份将在81年内累积到00。这正是Y2K问题的产生原因!我知道您届时将不会使用该代码,因此您可能不在乎。但是您应该始终编写代码,就像最终维护您的代码的人是知道您的住所的暴力精神病患者一样。


将您的新代码重新发布到另一个答案中

这是一个更好的解决方案,用于处理最大为999,999的next_id数字。

CREATE TRIGGER `update_request_missions_after_insert` BEFORE INSERT ON `request_missions`
FOR EACH ROW
BEGIN
  DECLARE next_id INT;
  SELECT COALESCE(MAX(id),0) + 1 INTO next_id FROM request_missions;
  IF next_id < 1000000 THEN
    SET NEW.rm_number = CONCAT(DATE_FORMAT(CURDATE(),'%y'), '-', LPAD(next_id, 6, '0'));
  ELSE
    SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'RM Number has reached capacity'
  END IF;
END

种族条件问题有所不同,使用MAX(id)查找最后一个ID时没有解决方案。问题是您可能有多个客户端同时插入到表中。每个客户的会话将为MAX(id)读取相同的值并使用它。

您应该阅读有关Race Conditions的信息。

您真正需要的是某种使用AUTO_INCREMENT值的方式,该值以线程安全的方式生成新的唯一值,并将该值复制到您的rm_number中。

不幸的是,无法在触发器中执行此操作。在INSERT触发器之前,尚未生成新的AUTO_INCREMENT值。在AFTER INSERT触发器期间,您无法修改NEW.rm_number,因为插入已经完成。我过去曾写过这本书:

答案 1 :(得分:0)

感谢M. Bill Karwin,现在代码更清晰了 对于一年级的问题,我将其更改为'%Y'。 现在的代码是:

CREATE TRIGGER `update_request_missions_after_insert` BEFORE INSERT ON `request_missions`
FOR EACH ROW
BEGIN
  DECLARE next_id INT;
  SELECT COALESCE(MAX(id),0) + 1 INTO next_id FROM request_missions;
  IF next_id BETWEEN 1 AND 9 THEN
    SET NEW.rm_number = CONCAT(DATE_FORMAT(CURDATE(),'%Y'), '-', '00000', next_id);
  ELSEIF next_id BETWEEN 10 AND 99 THEN
    SET NEW.rm_number = CONCAT(DATE_FORMAT(CURDATE(),'%Y'), '-', '0000', next_id);
  ELSEIF next_id BETWEEN 100 AND 999 THEN
    SET NEW.rm_number = CONCAT(DATE_FORMAT(CURDATE(),'%Y'), '-', '000', next_id);
  ELSEIF next_id BETWEEN 1000 AND 9999 THEN
    SET NEW.rm_number = CONCAT(DATE_FORMAT(CURDATE(),'%Y'), '-', '00', next_id);
  ELSEIF next_id BETWEEN 10000 AND 99999 THEN
    SET NEW.rm_number = CONCAT(DATE_FORMAT(CURDATE(),'%Y'), '-', '0', next_id);
  ELSEIF next_id BETWEEN 100000 AND 999999 THEN
    SET NEW.rm_number = CONCAT(DATE_FORMAT(CURDATE(),'%Y'), '-', next_id);
  END IF;
END
  

警告:这会遇到比赛条件。例如,如果两个   会话同时插入到表中,它们可能都   读取相同的next_id。

知道此触发器的目的是为添加的每行添加一个格式为“ YYYY-ID”的数字吗? YYYY:是当前年份 ID:是当前行的ID,格式如触发器中所示