Workaround for dynamic statements in stored procedures called from triggers

后端 未结 1 592
粉色の甜心
粉色の甜心 2020-12-18 10:49

Mysql PREPARE and EXECUTE statements can not be used in a stored procedure that is being called by a trigger. The result would be Error Code:

相关标签:
1条回答
  • 2020-12-18 11:17

    You can't run PREPARE/EXECUTE from inside a TRIGGER, but you can from an EVENT (if you are running MySQL 5.5 or greater).

    Here's an example of running PREPARE/EXECUTE from an EVENT:

    DROP TABLE IF EXISTS tbl1;
    DROP TABLE IF EXISTS tbl2;
    DROP TABLE IF EXISTS cmds;
    DROP PROCEDURE IF EXISTS proc;
    DROP TRIGGER IF EXISTS trig;
    
    CREATE TABLE tbl1 (i INT, v VARCHAR(255));
    CREATE TABLE tbl2 (i INT, v VARCHAR(255));
    
    CREATE TABLE cmds (
        id INT UNSIGNED NOT NULL AUTO_INCREMENT,
        done BOOL NOT NULL DEFAULT FALSE,
        cmd TEXT,
        PRIMARY KEY (id),
        INDEX (done, id)
    );
    
    DELIMITER //
    
    CREATE PROCEDURE proc()
    NOT DETERMINISTIC
    MODIFIES SQL DATA
    proc: BEGIN
        DECLARE b_not_found     BOOL DEFAULT FALSE;
        DECLARE i_id            INT UNSIGNED;
        DECLARE t_cmd           TEXT;
        DECLARE v_lock_name     VARCHAR(255) DEFAULT 'proc_lock';
    
        DECLARE cur CURSOR FOR
            SELECT id, cmd FROM cmds WHERE NOT done ORDER BY id;
    
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET b_not_found = TRUE;
    
        IF (NOT GET_LOCK(v_lock_name, 0)) THEN
            LEAVE proc;
        END IF;
    
        OPEN cur;
    
        loop1: LOOP
            FETCH cur INTO i_id, t_cmd;
            IF b_not_found THEN
                LEAVE loop1;
            END IF;
    
            SET @cmd = t_cmd;
    
            PREPARE stmt FROM @cmd;
            EXECUTE stmt;
            DROP PREPARE stmt;
    
            UPDATE cmds SET done = TRUE WHERE id = i_id;
        END LOOP;
    
        CLOSE cur;
    
        DO RELEASE_LOCK(v_lock_name);
    END;
    //
    
    CREATE TRIGGER trig
        BEFORE INSERT ON tbl1
        FOR EACH ROW
    BEGIN
        INSERT INTO cmds SET cmd = 
            CONCAT("INSERT INTO tbl2 SET i = ", -NEW.i, ", v = ", QUOTE(NEW.v));
    END;
    //
    
    DROP EVENT IF EXISTS evnt //
    
    CREATE EVENT evnt
    ON SCHEDULE 
    EVERY 1 SECOND
    DO
    BEGIN
        CALL proc();
    END;
    //
    
    DELIMITER ;
    
    SET GLOBAL event_scheduler = 1;
    

    Then running this:

    INSERT INTO tbl1 VALUES (UNIX_TIMESTAMP(), 'ex 1');
    DO SLEEP(2);
    INSERT INTO tbl1 VALUES (UNIX_TIMESTAMP(), 'ex 2');
    DO SLEEP(1);
    SELECT * FROM tbl2;
    

    will produce this output:

    +-------------+------+
    | i           | v    |
    +-------------+------+
    | -1348550619 | ex 1 |
    | -1348550621 | ex 2 |
    +-------------+------+
    2 rows in set (0.00 sec)
    

    If you don't want to use an EVENT, or wait the second or so for it to fire, you could add a CALL proc() after every command that would cause a TRIGGER to fire.

    0 讨论(0)
提交回复
热议问题