在库存系统的这个存储过程中,我想根据容器的类型及其容量(以千克为单位)处理产品的包装订单(以千克为单位)。 这最终将包装产品的存在存储在表“itemsenvasado”中。
我使用了两个光标,因为:
CREATE DEFINER=`soporte`@`localhost` PROCEDURE `insertarItemEnvasado`(
IN ordenID_in INT, -- ID de la Orden
IN lote_orden_in VARCHAR(45), -- Lote de la Orden de Envasado
IN bod_origenID_in INT, -- ID Bodega de Origen
IN bod_destinoID_in INT, -- ID Bodega de Destino
IN productoID_in INT, -- ID Producto a Envasar
IN cantidad_kg_envasar DECIMAL(10,2), -- Cantidad KG a Envasar
IN envaseID_in INT, -- ID Envase
IN capacidad_envase DECIMAL(10,2), -- Capacidad de Envase
IN cantidad_envase DECIMAL(10,2), -- Cantidad de Envases
IN codigo_final_in VARCHAR(45), -- Codigo del Producto Envasado Final
IN userID_in INT -- ID Usuario que hizo la Transaccion
)
BEGIN
DECLARE total_kg_envasar DECIMAL(10,2);
DECLARE total_envases INT;
DECLARE kg_restante DECIMAL(10,2);
DECLARE envases_restantes INT;
DECLARE precio_unitario DECIMAL(10,2);
DECLARE precio_por_kg DECIMAL(10,2);
DECLARE lote_id INT;
DECLARE lote_cantidad DECIMAL(10,2);
DECLARE lote_precio DECIMAL(10,2);
DECLARE envase_lote_id INT;
DECLARE envase_cantidad INT;
DECLARE precio_envase INT;
-- Cursor para Procesar los lotes de producto granel
DECLARE cur_lotes_envasado CURSOR FOR
SELECT id, precio, cantidadenvasar
FROM controlstock.lotesenvasado
WHERE ordenID = ordenID_in
ORDER BY fecInsert;
-- Cursor para Procesar los lotes de envases
DECLARE cur_envases CURSOR FOR
SELECT id, stock
FROM controlstock.existencias
WHERE productoID = envaseID_in AND bodegaID = bod_origenID_in AND status IN (1,2)
ORDER BY fecInsert ASC; -- Asumiendo que 'fecInsert' indica la antigüedad del lote
-- Manejadores de Salida
DECLARE EXIT HANDLER FOR NOT FOUND
BEGIN
CLOSE cur_lotes_envasado;
CLOSE cur_envases;
END;
-- PASO 1: Verificar existencia total de lotes seleccionados y obtener precio por kg
SELECT ifnull(sum(stock),0), AVG(precio)
INTO total_kg_envasar, precio_por_kg
FROM controlstock.lotesenvasado
WHERE ordenID = OrdenID_in;
-- Validar total KG para envasar
IF total_kg_envasar < cantidad_kg_envasar THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'No hay suficientes KG. para envasar';
END IF;
-- PASO 2: Verificar Existencia de Envases y sacar precio unitario
SELECT ifnull(sum(cantidad),0)
INTO total_envases
FROM controlstock.productobodega
WHERE productoID = envaseID_in AND bodegaID = bod_origenID_in;
set precio_envase = (SELECT AVG(precio) FROM controlstock.existencias WHERE productoID = envaseID_in AND bodegaID = bod_origenID_in AND status IN (1,2));
-- Validar total envases para envasar
IF total_envases < cantidad_envase THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'No hay suficientes envases para envasar';
END IF;
-- Setear Kg Restantes
SET kg_restante = cantidad_kg_envasar;
-- Setear Envases Restantes
SET envases_restantes = cantidad_envase;
-- PASO 3: Descontar del producto granel y de los envases
OPEN cur_lotes_envasado;
read_loop: LOOP
FETCH cur_lotes_envasado INTO lote_id, lote_precio, lote_cantidad;
-- Terminar Bucle cuando no quedan KG
IF kg_restante <= 0 THEN
LEAVE read_loop;
END IF;
IF lote_cantidad <= kg_restante THEN
UPDATE controlstock.lotesenvasado
SET stock = 0
WHERE id = lote_id;
SET kg_restante = kg_restante - lote_cantidad;
ELSE
UPDATE controlstock.lotesenvasado
SET stock = lote_cantidad - kg_restante
WHERE id = lote_id;
SET kg_restante = 0;
END IF;
END LOOP;
CLOSE cur_lotes_envasado;
OPEN cur_envases;
envase_loop: LOOP
FETCH cur_envases INTO envase_lote_id, envase_cantidad;
IF envases_restantes <= 0 THEN
LEAVE envase_loop;
END IF;
IF envase_cantidad <= envases_restantes THEN
UPDATE controlstock.existencias
SET stock = 0
WHERE id = envase_lote_id;
SET envases_restantes = envases_restantes - envase_cantidad;
ELSE
UPDATE controlstock.existencias
SET stock = envase_cantidad - envases_restantes AND status = 2
WHERE id = envase_lote_id;
SET envases_restantes = 0;
END IF;
END LOOP;
CLOSE cur_envases;
-- PASO 4: Calcular el precio unitario del producto envasado
SET precio_unitario = precio_por_kg * capacidad_envase + precio_envase;
-- PASO 5: Añadir el producto envasado al inventario
INSERT INTO controlstock.itemsenvasado (documentoID, productoID, bodegaID, lote, unidadID, precio, cantidad, stock, userID, status)
VALUES (ordenID_in, productoID_in, bod_destinoID_in, lote_orden_in, 4, precio_unitario, cantidad_envase, cantidad_envase, userID_in, 1);
END
当我运行它时,它仅执行第一个光标,然后出现错误
ER_SP_CURSOR_NOT_OPEN:光标未打开
我尝试移动光标,但在 MySQL Workbench 中出现错误
DECLARE 在此位置无效,期待 END
当通过第一个光标获得 NOT FOUND 时,将触发 EXIT 处理程序。执行其语句(关闭游标)后,流程EXIT(离开)您的存储过程。
您必须使用 CONTINUE 处理程序,它设置一个变量,但将流程返回到触发点,在 FETCH 之后立即检查变量,如果设置了变量,则退出循环。
示意图:
CREATE PROCEDURE ..
DECLARE done INT DEFAULT FALSE;
DECLARE CURSOR cursor1 ...
DECLARE CURSOR cursor2 ...
DECLARE CONTINUE HANDLER FOR NOT FOUND BEGIN SET done = TRUE; END;
BEGIN
...
OPEN cursor1;
DO
FETCH cursor1 INTO ...
IF done THEN LEAVE ... -- check does 1st cursor is empty
-- if true then leave cycle but continue execution
...
LOOP;
SET done = FALSE; -- reset variable value for 2nd cursor checking
...
OPEN cursor2;
DO
FETCH cursor2 INTO ...
IF done THEN LEAVE ...
...
LOOP;
...