【问题标题】:MySQL stored procedure cursor for prepared statements用于准备好的语句的 MySQL 存储过程游标
【发布时间】:2014-07-30 20:56:44
【问题描述】:

我有两张桌子:

people_en: id, name
people_es: id, name

(请不要为规范化而烦恼。设计已规范化。表格比这复杂得多 但这只是简化我的问题的一种方式)。

然后我有一个存储过程:

CREATE PROCEDURE myproc(lang char(2))
BEGIN
    set @select = concat('SELECT * FROM ', lang, ' limit 3');
    PREPARE stm FROM @select;
    EXECUTE stm;
    DEALLOCATE PREPARE stm;
    SET @cnt = FOUND_ROWS(); 
    SELECT @cnt;
    IF @cnt = 3 THEN
        //Here I need to loop through the rows
    ELSE
        //Do something else
    END IF;
   END$$

或多或少,程序中的逻辑是:

如果 select 给出了 3 行,那么我们必须遍历这些行并对每行中的值做一些事情。 否则 somwthing else (不重要什么,但我放这个是为了让你明白我需要有 循环前的 if 语句。

我已经看到并阅读了有关游标的信息,但找不到很多由 concat 创建的选择(这有关系吗?) 尤其是用准备好的语句创建的。 如何遍历结果列表并使用每一行的值? 谢谢。

【问题讨论】:

    标签: mysql stored-procedures


    【解决方案1】:

    我有一些坏消息和好消息要告诉你。

    首先是坏消息。

    MySQL 手册说游标不能用于动态语句 用 PREPARE 和 EXECUTE 准备和执行。声明为 在创建游标时会检查游标,因此该语句不能 动态的。

    所以到目前为止还没有动态游标......在这里你需要这样的东西。

    但现在好消息:至少有两种方法可以绕过它 - 使用 vw 或 tbl。

    下面我重写了您的代码并应用视图来制作“动态”光标。

    DELIMITER //
    
    DROP PROCEDURE IF EXISTS myproc;
    CREATE PROCEDURE myproc(IN lang VARCHAR(400))
    
    BEGIN
    
        DECLARE c VARCHAR(400);
        DECLARE done BOOLEAN DEFAULT FALSE;
        DECLARE cur CURSOR FOR SELECT name FROM vw_myproc;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
    
        SET @select = concat('CREATE VIEW vw_myproc as SELECT * FROM ', lang, ' limit 3');
        PREPARE stm FROM @select;
        EXECUTE stm;
        DEALLOCATE PREPARE stm;
    
        SET @select = concat('SELECT * FROM ', lang, ' limit 3');
        PREPARE stm FROM @select;
        EXECUTE stm;
        DEALLOCATE PREPARE stm;
    
        SET @cnt = FOUND_ROWS(); 
        SELECT @cnt;
        IF @cnt = 3 THEN
              OPEN cur;
              read_loop: LOOP
                FETCH cur INTO c;
                IF done THEN
                  LEAVE read_loop;
                END IF;
    
                #HERE YOU CAN DO STH WITH EACH ROW e.g. UPDATE; INSERT; DELETE etc
                SELECT c;
    
              END LOOP read_loop;
              CLOSE cur;
              DROP VIEW vw_myproc;
        ELSE
            SET c = '';
        END IF;
    
    END//
    
    DELIMITER ;
    

    并测试程序:

    CALL myproc('people_en');
    

    【讨论】:

    • 对于这种情况,我假设将在运行存储的例程时检查 CREATE TEMPORARY TABLE 权限,而不是在创建它时。对吗?
    【解决方案2】:

    @clickstefan,你会遇到两个或多个用户同时尝试执行你的脚本的问题。第二个用户将收到错误消息“查看 vw_myproc 已存在”:

    SET @select = concat('CREATE VIEW vw_myproc as SELECT * FROM ', lang, ' limit 3');
    

    解决方案是临时表 - 它仅在当前连接的生命周期内存在,用户可以同时创建具有相同名称的临时表。因此,代码可能如下所示:

    DROP TABLE IF EXISTS vw_myproc;
    SET @select = concat('CREATE TEMPORARY TABLE vw_myproc AS SELECT * FROM ', lang, ' limit 3');
    

    【讨论】:

    • 对于这种情况,我假设将在运行存储例程时检查 CREATE TEMPORARY TABLE 权限,而不是在创建时检查?
    • 创建视图也可以,我们需要根据时间戳创建它,程序执行后它会被删除。在下面的语句中: DROP TABLE IF EXISTS vw_myproc;表不会创建,但数据可能会混合。如果多个用户同时调用。
    猜你喜欢
    • 1970-01-01
    • 2010-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-03
    • 1970-01-01
    • 2015-06-23
    相关资源
    最近更新 更多