Вложенные курсоры в Mysql - PullRequest
13 голосов
/ 14 марта 2012

У меня есть три таблицы.
Проект (Id), атрибут (Id), атрибут_проекта (Id, project_id, attribute_id) .

Я хочу создать записи в project_attribute таблица с использованием всех атрибутов из attribute таблица для каждого проекта из project таблица.

Для создания таких записей я использую следующую процедуру хранения.

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 ;

Но эта процедура создает записи только для 1 проекта в таблице project_attribute, хотя в таблице Project содержится 50 проектов.Ожидаемое количество записей: count (projectId) * count (attributeId).

Ответы [ 3 ]

32 голосов
/ 19 марта 2012

Попробуйте, это, несомненно, решит вашу проблему.

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 ;
7 голосов
/ 14 марта 2012

Прямо говоря, вложенные курсоры (обычно) ужасная идея. Вы можете получить то, что вы хотите напрямую, без использования курсора, используя обычный CROSS JOIN.

INSERT INTO proj_attr (project, attribute)
    SELECT p.id AS projectid, a.id AS attributeid
    FROM project p CROSS JOIN attribute a;
4 голосов
/ 04 января 2018

После первой итерации во внутреннем курсоре переменная 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...