Lo normal al usar procedimientos almacenados en MySQL es trabajar con un solo CURSOR:
BEGIN DECLARE v_foo INT; DECLARE no_more_rows BOOLEAN:= FALSE; -- variable de control del loop DECLARE cursor1 CURSOR FOR -- definimos el cursor SELECT col1 FROM table; DECLARE CONTINUE HANDLER FOR NOT FOUND -- Capturamos el final del cursor y cambiamos la variable de control set no_more_rows := TRUE; OPEN cursor1; -- Ejecutamos la query del cursor LOOP1: loop fetch cursor1 into v_foo; -- Mapeamos la actual tupla en variables if no_more_rows then close cursor1; leave LOOP1; end if; -- -- Codigo del procedimiento -- end loop LOOP1; end;
El CURSOR se procesa iterando con un LOOP. Para cada iteración, guardamos los valores retornados por la query en variables. Cuando el cursor llega al final de la query, no devuelve mas resultados, el manejador(handler) de NOT FOUND salta y cambia la variable de control del LOOP, rompiendo la iteración.
El problema viene cuando necesitamos tener dos iteraciones sobre cursores diferentes y que estén anidadas. Por la definición de handler, éstos solo pueden ser definidos DESPUES de haber declarado TODOS los cursores, esto quiere decir que un manejador no está relacionado con un cursor en particular. ¿Como controlar entonces dos cursores?:
BEGIN DECLARE no_more_rows BOOLEAN DEFAULT false; DECLARE v_col1 INT; DECLARE v_col2 INT; DECLARE cursor1 CURSOR FOR SELECT col1 FROM table; DECLARE cursor2 CURSOR FOR SELECT col2 FROM table2; DECLARE CONTINUE HANDLER FOR NOT FOUND SET v_done := true; OPEN cursor1; LOOP1: LOOP FETCH cursor1 INTO v_col1; IF v_done THEN CLOSE cursor1; LEAVE LOOP1; END IF; OPEN cursor2; LOOP2: LOOP FETCH cursor2 INTO v_col2; IF v_done THEN SET v_done := false; CLOSE cursor2; LEAVE LOOP2; END IF; END LOOP LOOP2; END LOOP LOOP1; END;
La iteración exterior, sigue siendo una iteración normal con un sistema de control normal. El cambio está en el control de la iteración interior, ya que cuando se cumple, además de cerrar el cursor y salir del loop, volvemos a cambiar la variable de estado v_done, para que el loop exterior siga con su iteración.
Un ejemplo practico que calcula el total pedido de un fichero de lineas de pedido y lo inserta en un archivo de totales de pedido:
DELIMITER $$
CREATE DEFINER=`root`@`localhost` PROCEDURE `totalespedido`()
BEGIN
DECLARE a,b,c INT;
DECLARE pre1 decimal(7,2);
DECLARE var_done INT DEFAULT 0;
DECLARE codigo1 int;
declare total decimal(8,2);
DECLARE cur1 CURSOR FOR SELECT pedidos.id_pedido, pedidos.id_producto, pedidos.cantidad FROM pedidos WHERE pedidos.id_pedido=codigo1;
declare cur2 cursor for select DISTINCT pedidos.id_pedido from pedidos;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET var_done=1;
open cur2;
get_b:loop
fetch cur2 into codigo1;
if var_done=1 THEN leave get_b;
end IF;
set total = 0;
OPEN cur1;
get_a:loop
FETCH cur1 INTO a, b, c;
if var_done=1 THEN
set var_done=0;
close cur1;
leave get_a;
end IF;
select precio into pre1 from articulos WHERE articulos.id_articulo=b;
set total=total+(pre1*c);
END LOOP get_a;
update totalpedido set cantidad=total where id_pedido=codigo1;
end loop get_b;
close cur2;
END$$
DELIMITER ;
Fuente :http://www.thegameofcode.com/2012/08/anidar-iteraciones-con-cursores-mysql.html
Comentarios
Publicar un comentario