mysql - 从触发器调用的存储过程中的动态语句的解决方法

标签 mysql sql stored-procedures triggers

Mysql PREPAREEXECUTE 语句不能在触发器调用的存储过程中使用。结果将是 错误代码:1336。存储函数或触发器中不允许使用动态 SQL

有人知道解决这个问题的可行方法吗?

最佳答案

您不能从 TRIGGER 中运行 PREPARE/EXECUTE,但您可以从 EVENT 中运行(如果您运行的是 MySQL 5.5 或更高版本)。

这是从 EVENT 运行 PREPARE/EXECUTE 的示例:

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;

然后运行这个:

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;

将产生这个输出:

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

如果您不想使用 EVENT,或等待它触发一秒左右,您可以在每个命令后添加一个 CALL proc()这会导致 TRIGGER 触发。

关于mysql - 从触发器调用的存储过程中的动态语句的解决方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12575631/

相关文章:

MySQL:在具有现有记录的表上添加索引

c# - 在 C# 中将数据加载到 DataTable 会出现 "Unknown SQL type - 0"错误

c# - 重命名 ASP.NET Identity 表时出现重复的外键

mysql - 如何解决子查询返回多于 1 行的错误

sql - 是否可以将一组参数保存并重用为 SQL Server Express 2005 中的代码块?

mysql - [MySQL] : Stored Procedure's and select statements

c# - 使用 c# 和 mysql 更新表

sql - 如何按计数行选择数据顺序

sql-server - 使用带有两个表的 SQL View 在 MS-Access 表单上编辑数据的任何方法

MySQL 查询未使用多个 AND 运算符进行编译