【问题标题】:Nested Cursors in MysqlMysql中的嵌套游标
【发布时间】:2012-03-14 10:15:01
【问题描述】:

我有三个表。
Project(Id)、attribute(Id)、project_attribute(Id, project_id, attribute_id)

我想通过使用 attribute 表中的所有属性到 project 表中的每个项目在 project_attribute 表中创建记录。

要创建此类记录,我使用以下存储过程。

DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `proj_attr`()
BEGIN   
    DECLARE proj_done, attribute_done BOOLEAN DEFAULT FALSE;    
    declare attributeId int(11) default 0;
    declare  projectId int(11) default 0;
    DECLARE curProjects CURSOR FOR SELECT id FROM project order by id;  
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET proj_done = TRUE;

    OPEN curProjects;
    cur_project_loop: LOOP
    FETCH FROM curProjects INTO projectId;

        IF proj_done THEN
        CLOSE curProjects;
        LEAVE cur_project_loop;
        END IF;

        BLOCK2: BEGIN
        DECLARE curAttribute CURSOR FOR SELECT id FROM attribute order by id;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET attribute_done = TRUE;
        OPEN curAttribute; 
        cur_attribute_loop: LOOP
        FETCH FROM curAttribute INTO attributeId;   
            IF attribute_done THEN
            CLOSE curAttribute;
            LEAVE cur_attribute_loop;
            END IF; 
            insert into project_attribute_value(project_id, attribute_id)
                values(projectId, attributeId); 
        END LOOP cur_attribute_loop;
        END BLOCK2;
    END LOOP cur_project_loop;


    END$$

DELIMITER ;

但是,即使 Project 表中有 50 个项目,此过程也仅为 project_attribute 表中的 1 个项目创建记录。预期记录数为 count(projectId)*count(attributeId)。

【问题讨论】:

    标签: mysql database-cursor


    【解决方案1】:

    试试这个,一定能解决你的问题。

    DELIMITER $$
    CREATE DEFINER=`root`@`localhost` PROCEDURE `proj_attr`()
    BEGIN   
        DECLARE proj_done, attribute_done BOOLEAN DEFAULT FALSE;    
        declare attributeId int(11) default 0;
        declare  projectId int(11) default 0;
        DECLARE curProjects CURSOR FOR SELECT id FROM project order by id;  
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET proj_done = TRUE;
    
        OPEN curProjects;
        cur_project_loop: LOOP
        FETCH FROM curProjects INTO projectId;
    
            IF proj_done THEN
            CLOSE curProjects;
            LEAVE cur_project_loop;
            END IF;
    
            BLOCK2: BEGIN
            DECLARE curAttribute CURSOR FOR SELECT id FROM attribute order by id;
            DECLARE CONTINUE HANDLER FOR NOT FOUND SET attribute_done = TRUE;
            OPEN curAttribute; 
            cur_attribute_loop: LOOP
            FETCH FROM curAttribute INTO attributeId;   
                IF proj_done THEN
                set proj_done = false;
                CLOSE curAttribute;
                LEAVE cur_attribute_loop;
                END IF; 
                insert into project_attribute_value(project_id, attribute_id)
                    values(projectId, attributeId); 
            END LOOP cur_attribute_loop;
            END BLOCK2;
        END LOOP cur_project_loop;
    
    
        END$$
    
    DELIMITER ;
    

    【讨论】:

    【解决方案2】:

    坦率地说,嵌套游标(通常)是一个糟糕的主意。您可以使用普通的CROSS JOIN 直接获取您想要的内容,而无需使用光标。

    INSERT INTO proj_attr (project, attribute)
        SELECT p.id AS projectid, a.id AS attributeid
        FROM project p CROSS JOIN attribute a;
    

    【讨论】:

    • 有关游标通常被认为是一个坏主意的更多信息,请参阅stackoverflow.com/questions/58141/…stackoverflow.com/questions/287445/…
    • 真的没有比赛。游标需要更多的代码,这比简单的连接更难理解。而在 MySQL 的情况下,性能简直糟糕透顶。真的,真的没有理由使用光标。
    • 您确定在将嵌套光标标记为坏主意之前,您考虑过所有用途?我怀疑。
    • 我必须对动态表名运行查询(大规模的 WP 多站点安装,1000 个表中的 10 个)。外部游标获取表名,需要内部游标来处理对这些表的动态查询。可能有更好的方法(无需借助外部代码),但我没有看到。
    【解决方案3】:

    内部游标内的第一次迭代后,“attribute_done”变量设置为“true”。对于下一次迭代,它仍然是“真实的”。

    这会导致每次下一次迭代都跳过内部循环。

    一个示例嵌套光标如下所示。


    CREATE TABLE `parent` (
      `a` int(11) DEFAULT NULL
    ) ENGINE=InnoDB
    CREATE TABLE `child` (
      `a` int(11) DEFAULT NULL,
      `b` varchar(20) DEFAULT NULL
    ) ENGINE=InnoDB
    
    insert into parent values (1),(2),(3);
    
    insert into child values (1,'a'),(1,'b'),(2,'a'),(2,'b'),(3,'a'),(3,'b');
    
    ----------------------------------
    drop procedure if exists nestedCursor;
    create procedure nestedCursor()
    BEGIN   
        DECLARE done1, done2 BOOLEAN DEFAULT FALSE;  
        DECLARE parentId,childId int;
        DECLARE childValue varchar(30);
    
        DECLARE cur1 CURSOR FOR SELECT a FROM parent;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET done1 = TRUE;
    
        open cur1;
        loop1: LOOP
        FETCH FROM cur1 INTO parentId;
        IF done1 THEN
            CLOSE cur1;
            LEAVE loop1;
        END IF;
    
        BLOCK1 : BEGIN
        DECLARE cur2 CURSOR FOR SELECT a,b FROM child where a = parentId;
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET done2 = TRUE;
    
    
        open cur2;
        loop2 : LOOP
        FETCH FROM cur2 INTO childId,childValue;  
            if done2 THEN
            CLOSE cur2;
            SET done2 = FALSE;
            LEAVE loop2;
            end if;
            select parentId,childId,childValue;
    
        END LOOP loop2;
        END BLOCK1;
        END loop loop1;
    END;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-01-18
      • 2023-03-25
      • 1970-01-01
      • 2012-06-15
      • 1970-01-01
      • 2014-04-01
      • 1970-01-01
      • 2015-10-09
      相关资源
      最近更新 更多