MySQL在更新时在同一个表上触发

时间:2015-01-01 22:58:09

标签: mysql phpmyadmin

我有一个带有订单号的startlists表。每个test_of_event_id(在一个名为subscriptions的相关表中找到),有一个像(1,2,3,4,5,...)这样的序列创建一个很好的列表。

当列表编号发生变化时,我希望这个列表易于维护(使用phpmyadmin)。 我在更新

上触发了表的起始列表
  CREATE TRIGGER `edit_startlists` BEFORE UPDATE ON `startlists`
 FOR EACH ROW BEGIN
    IF (NEW.order_number != OLD.order_number) THEN
    IF (NEW.subscription_id!=OLD.subscription_id) THEN
        signal sqlstate '45000';
    ELSE
        IF (NEW.order_number < OLD.order_number) THEN
            /*number has become smaller --> everthing equal to or bigger then new , and smaller then old --> +1 */
            UPDATE startlists
            JOIN subscriptions 
            ON subscriptions.id = startlists.subscription_id
            SET order_number = (order_number+1)

            WHERE order_number >= NEW.order_number 
            AND order_number < OLD.order_number
            AND subscriptions.test_of_event_id=(SELECT sub.test_of_event_id FROM subscriptions sub WHERE sub.id=OLD.subscription_id);

        ELSE
            /*number has become bigger --> everthing equal to or smaller then new , and bigger then old --> -1 */
            UPDATE startlists
            JOIN subscriptions 
            ON subscriptions.id = startlists.subscription_id
            SET order_number = (order_number-1)

            WHERE order_number <= NEW.order_number 
            AND order_number > OLD.order_number
            AND subscriptions.test_of_event_id=(SELECT sub.test_of_event_id FROM subscriptions sub WHERE sub.id=OLD.subscription_id);
        END IF;
    END IF;
END IF;

END

创建触发器没有问题,只有当订单号发生变化时才会出现此错误。

#1442 - Can't update table 'startlists' in stored function/trigger because it is already used by statement which invoked this stored function/trigger.

我使用的是InnoDB,这不应该只锁定一行吗?

我想出了别的东西: 我向表启动列表添加了一个布尔字段(procedure_check)以检查以避免递归。然后做了一个触发器,在必要时调用该过程:

   DROP TRIGGER IF EXISTS edit_startlist;
DELIMITER $$
CREATE TRIGGER edit_startlist AFTER UPDATE ON startlists
FOR EACH ROW
  BEGIN
    DECLARE current_test_of_event_id INT;
    DECLARE max_order_number INT;
    IF (NEW.order_number != OLD.order_number)
    THEN
      IF (NEW.procedure_edit = 0)
      THEN
        IF (NEW.subscription_id != OLD.subscription_id)
        THEN
          SIGNAL SQLSTATE '45000';
        ELSE
          SET current_test_of_event_id = (SELECT test_of_event_id
                                          FROM (SELECT sub.test_of_event_id
                                                FROM subscriptions sub
                                                WHERE sub.id = OLD.subscription_id
                                               ) AS current_subscription
          );

          SET max_order_number = (SELECT MAX(s.order_number)
                                  FROM startlists s, subscriptions sub
                                  WHERE s.subscription_id = sub.id
                                        AND sub.test_of_event_id = current_test_of_event_id
          );
          IF (NEW.order_number <= 0 OR NEW.order_number > max_order_number)
          THEN
            SIGNAL SQLSTATE '45000';
          ELSE
            CALL adjust_startlist(NEW.order_number,OLD.order_number,current_test_of_event_id);
          END IF;
        END IF;
      ELSE
        /*this edit is made by procedure, change the procedure boolean back to 0, so it is ready for a next manual edit*/
        UPDATE startlists SET
          procedure_edit=0 WHERE
          id=NEW.id;
      END IF;
    END IF;
  END$$

DELIMITER ;

程序:

 DELIMITER $$
CREATE PROCEDURE adjust_startlist
  (IN new_order_number INT UNSIGNED, IN old_order_number INT UNSIGNED, IN current_test_of_event_id INT UNSIGNED)
MODIFIES SQL DATA
  BEGIN
    IF (new_order_number < old_order_number)
    THEN
    /*number has become smaller --> everthing equal to or bigger then new , and smaller then old --> +1 */

      UPDATE startlists
        JOIN subscriptions
          ON subscriptions.id = startlists.subscription_id
      SET order_number  = (order_number + 1),
        procedure_check = 1

      WHERE order_number >= new_order_number
            AND order_number < old_order_number
            AND subscriptions.test_of_event_id = current_test_of_event_id;
        /* UPDATE startlists SET order_number = NEW.*/

    ELSE
    /*number has become bigger --> everthing equal to or smaller then new , and bigger then old --> -1 */
      UPDATE startlists
        JOIN subscriptions
          ON subscriptions.id = startlists.subscription_id
      SET order_number  = (order_number - 1),
        procedure_check = 1

      WHERE order_number <= new_order_number
            AND order_number > old_order_number
            AND subscriptions.test_of_event_id = current_test_of_event_id;
    END IF;
  END$$
DELIMITER ;

当我执行时,添加了过程和触发器,没有语法错误。 但是当我尝试修改订单号时,我收到了这个错误:

#1415 - Not allowed to return a result set from a trigger 

我不知道是什么导致了这个错误,更不用说如何解决了。我已经对它进行了研究,但我遇到的所有例子都不适用于此。

1 个答案:

答案 0 :(得分:1)

通常,您不能在同一查询中使用正在更新的表。您可以在from子句中多次包含该表,但您不是这样做的。但是,如果您足够嵌套查询,则会有例外。

尝试更改此行:

subscriptions.test_of_event_id = (SELECT sub.test_of_event_id
                                  FROM subscriptions sub
                                  WHERE sub.id=OLD.subscription_id
                                 )

为:

subscriptions.test_of_event_id = (SELECT test_of_event_id
                                  FROM (SELECT sub.test_of_event_id
                                        FROM subscriptions sub
                                        WHERE sub.id=OLD.subscription_id
                                       ) s
                                 )

MySQL实际上实现了子查询,这使得这允许。