Foros del Web » Programación para mayores de 30 ;) » Bases de Datos General » Mysql »

problemilla Procedure

Estas en el tema de problemilla Procedure en el foro de Mysql en Foros del Web. Cordial Saludo.. Desde hace unos dias he tenido problemas con un procedimiento en mysql en verdad llevo muchas muchas horas intentando saber cual es el ...
  #1 (permalink)  
Antiguo 22/07/2014, 17:07
 
Fecha de Ingreso: febrero-2013
Ubicación: Cali Colombia
Mensajes: 118
Antigüedad: 11 años, 8 meses
Puntos: 0
Exclamación problemilla Procedure

Cordial Saludo..


Desde hace unos dias he tenido problemas con un procedimiento en mysql
en verdad llevo muchas muchas horas intentando saber cual es el problema que no funciona mi procedure, en el codigo veran entre comentarios un handler que pense que era por falta de excepciones pero igual no me funciono. Les dejo en contexto las tablas que use y unas inserciones de prueba y el procedimiento en este link de pastebin http://pastebin.com/FeKuvAQB si alguien podría ayudarme a entender cual es el problema le agradecería...
  #2 (permalink)  
Antiguo 22/07/2014, 17:10
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 17 años
Puntos: 2658
Respuesta: problemilla Procedure

Postea el código completo aquí en el foro.
No tenemos por qué andar descargando o buscando en otros sitios, cuando do en este tienes los recursos para mostrar lo que se necesita.
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #3 (permalink)  
Antiguo 22/07/2014, 18:29
 
Fecha de Ingreso: febrero-2013
Ubicación: Cali Colombia
Mensajes: 118
Antigüedad: 11 años, 8 meses
Puntos: 0
Busqueda procedimiento en Mysql

Saludos, tengo el siguiente procedure el cual tengo problemas para que funcione.

Si requieren saber cuales son las tablas, les dejo el siguiente link http://pastebin.com/9XrKZhkH como anexo al problema, no lo publico aqui
porque es extenso y puede ser molesto para alguno.


USE almacen3;
-- Procedimiento con dos parametros de entrada, fecha inicio y fecha final el cual
-- selecciona todas las facturas que estan entre esas fechas. En otro loop hace una
-- busqueda en la tabla que esta relacionada con factura "ord_compra" y selecciona
-- la columna que tiene el nº de factura. En el segundo LOOP maneja un joins triple
-- con el fin de seleccionar el nombre, costo y codigo de los productos que fueron
-- vendidos. Por ultimo se crea una tabla en memoria donde almacenara los valores
-- que se consultaron con el fin de realizar informes mas sencillos.

Código MySQL:
Ver original
  1. delimiter //
  2. CREATE PROCEDURE devuelve_informe_venta(IN fecha_inicio DATE, IN fecha_final DATE)
  3.  
  4. BEGIN    
  5.    DECLARE fecha_factura DATE;
  6.    DECLARE codigo_facura INT DEFAULT 0;
  7.    DECLARE cod_producto INT DEFAULT 0;
  8.    DECLARE costo DOUBLE DEFAULT 0.0;
  9.    DECLARE total_registro INT DEFAULT 0;
  10.    DECLARE nombre_producto VARCHAR(20);
  11.    DECLARE nit INT DEFAULT 0;
  12.    DECLARE crs_num_fact CURSOR FOR SELECT fact_cod_fac,fact_fecha FROM Factura
  13.                                                 WHERE fact_fecha >= fecha_inicio AND fact_fecha <= fecha_final;
  14.    DECLARE crs_cod_prod CURSOR FOR SELECT ord_cod_zap,pro_nomb,cal_cost,pro_nit FROM ord_compra oc, Producto p,Calzado c
  15.                                           WHERE ord_cod_fact = codigo_factura;
  16.    DECLARE cursor_suma CURSOR FOR SELECT COUNT(*) FROM Factura WHERE fact_fecha >= fecha_inicio AND fact_fecha <= fecha_final;
  17.  --  declare Continue Handler FOR SQLSTATE   'un problema encontrado 1064' as msn;
  18.    DROP TABLE IF EXISTS informe;
  19.    CREATE TABLE informe(num INT(5) AUTO_INCREMENT PRIMARY KEY,fecha DATE,cod_cal INT(10),nomb_cal VARCHAR(20),costo DECIMAL(6,2),nit INT(10))ENGINE = MEMORY;
  20.  
  21.    OPEN cursor_suma;
  22.    FETCH cursor_suma INTO total_registro;
  23.    OPEN crs_num_fact;
  24.    BEGIN
  25.          crs_num_fact_loop:LOOP -- LOOP donde recorrera las fechas que se filtraron
  26.          FETCH crs_num_fact INTO cod_factura,fecha_factura;
  27.          SET total_registro = total_registro + 1; -- contador de uno en uno.
  28.          crs_cod_pro_loop:LOOP -- segundo LOOP donde realiza el la busqueda de nombre de productos, codigo y costo.
  29.             OPEN crs_cod_prod;  -- abre el LOOP
  30.             FETCH crs_cod_prod INTO cod_producto, nombre_producto,costo,nit;
  31.                     INSERT INTO informe VALUES('',fecha_factura,cod_producto,nombre_producto,costo,nit);
  32.                         IF total_registro >= crs_num_fact THEN -- finaliza la interaccion de ambos LOOP pues ya ha llegado al final del total de facturas a interar.
  33.                           CLOSE csr_num_fact;
  34.                           CLOSE csr_cod_prod;
  35.                           iterate crs_num_fact_loop; -- cierra el primer LOOP
  36.                           iterate crs_cod_pro_loop; -- cierra el segundo LOOP
  37.                         END IF;
  38.          END LOOP crs_cod_pro_loop;
  39.          END LOOP crs_fact_loop;
  40.     END;
  41.  END;//
  42. delimiter ;
  #4 (permalink)  
Antiguo 23/07/2014, 06:16
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 17 años
Puntos: 2658
Respuesta: problemilla Procedure

Por empezar, al menos hay un error conceptual: No puedes cerrar las iteraciones cruzadas.
Si abres LOOP 1 y LOOP 2, estando el segundo dentro del primero , siempre deben cerrarse o enviar a ciclo nuevo en el orden inverso, es decir respetando el anidamieento del proceso.
Me refiero puntualmente a esto:
Código MySQL:
Ver original
  1. crs_num_fact_loop:LOOP
  2. ...
  3. crs_cod_pro_loop:LOOP -- segundo LOOP donde realiza el la busqueda de nombre de productos, codigo y costo.
  4.         ...
  5.             IF total_registro >= crs_num_fact THEN
  6.               ...
  7.               iterate crs_num_fact_loop; -- cierra el primer LOOP <-- Cierre cruzado. Orden incorrecto
  8.               iterate crs_cod_pro_loop; -- cierra el segundo LOOP
  9.             END IF;
  10. END LOOP crs_cod_pro_loop;
  11. END LOOP crs_fact_loop;
Adicionalmente, tienes esto:
Código MySQL:
Ver original
  1. OPEN cursor_suma;
  2.    ...
  3.    OPEN crs_num_fact;
  4.    BEGIN
  5.          crs_num_fact_loop:LOOP -- LOOP donde recorrera las fechas que se filtraron
  6.          ...
  7.             crs_cod_pro_loop:LOOP
  8.             OPEN crs_cod_prod;  -- abre el LOOP <.. No abre un loop, abre un CURSOR
  9.             FETCH crs_cod_prod INTO cod_producto, nombre_producto,costo,nit;
  10.                     INSERT INTO informe VALUES('',fecha_factura,cod_producto,nombre_producto,costo,nit);
  11.                         IF total_registro >= crs_num_fact THEN
  12.                           CLOSE csr_num_fact; -- <-- Caso 1
  13.                           CLOSE csr_cod_prod; -- <-- Caso 2
  14.                           ...
  15.                         END IF;
  16.          END LOOP crs_cod_pro_loop;
  17.          END LOOP crs_fact_loop;
  18.     END;
Caso 1: Cierras un CURSOR que abriste fuera del loop. El loop fallará por el cursor cerrado
Caso 2: Cierras un CURSOR que abriste dentro del loop. Cada iteración vovlera a abrirlo y reeejecutar la query desde el principio. No tiene sentido.

Además no has dejado el HANDLER, por lo que cualquier fallo de cualquier tipo producirá problemas no administrados.

En las FAQs de MySQL yo postee un ejemplo detallado hace ya bastante tiempo, que te puede servir de referencia para ver mejor el tema. En cualquier caso, no te olvides que en los anidamientos se debe respetar la secuencia de aperturas y cierres.

Finalmente, un consejo: En muchísimos casos he visto que una secuencia de queries a tablas temporales son más eficientes que usar cursores. Yo estimo (no puedo hacer pruebas en este momento porque no estoy en casa), que tal vez rediseñando las consultas podrías llegar a obtener iguales resultados sin necesidad de ellos.
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)
  #5 (permalink)  
Antiguo 23/07/2014, 15:09
 
Fecha de Ingreso: febrero-2013
Ubicación: Cali Colombia
Mensajes: 118
Antigüedad: 11 años, 8 meses
Puntos: 0
Saludos, gracias por la la informacion, fue de mucha ayuda, sin embargo no logro
dominar este codigo. Intente hacer manejar un Handler pero no supe hacerlo doble por mi falta de experiencia en mysql.
Intente trabajar con un LOOP de tipo REPEAT pero el workBench me recomienda usar
Repeatable no se porque pero lei algo que las tablas cuando es de tipo InnoDB es con Repeatable.

Le pido mil disculpas por la molestia, por favor colaborarme nuevamente en este codigo.

Mil gracias..

Código MySQL:
Ver original
  1. delimiter //
  2. CREATE PROCEDURE devuelve_informe_venta(IN fecha_inicio DATE, IN fecha_final DATE)
  3.  
  4. BEGIN    
  5.    DECLARE fecha_factura DATE;
  6.    DECLARE codigo_facura INT DEFAULT 0;
  7.    DECLARE cod_producto INT DEFAULT 0;
  8.    DECLARE costo DOUBLE DEFAULT 0.0;
  9.    DECLARE total_registro INT DEFAULT 0;
  10.    DECLARE acumulador INT DEFAULT 0;
  11.    DECLARE nombre_producto VARCHAR(20);
  12.    DECLARE nit INT DEFAULT 0;
  13.   DECLARE accion INT DEFAULT 0;
  14.   DECLARE v_last_row_fetched,v_last_row_fetched2 INT DEFAULT 0;
  15.   DECLARE CONTINUE HANDLER FOR NOT FOUND SET  v_last_row_fetched=1;
  16.   DECLARE crs_num_fact CURSOR FOR SELECT fact_cod_fac,fact_fecha FROM Factura
  17.                                                 WHERE fact_fecha >= fecha_inicio AND fact_fecha <= fecha_final;
  18.  
  19.   DECLARE crs_cod_prod CURSOR FOR SELECT ord_cod_zap,pro_nomb,cal_cost,pro_nit FROM ord_compra oc, Producto p,Calzado c
  20.                                           WHERE ord_cod_fact = codigo_factura;
  21.    DECLARE cursor_suma CURSOR FOR SELECT COUNT(*) FROM Factura WHERE fact_fecha >= fecha_inicio AND fact_fecha <= fecha_final;
  22.    CREATE temporary TABLE informe (num INT(5) AUTO_INCREMENT PRIMARY KEY,fecha DATE,cod_cal INT(10),nomb_calVARCHAR(20),costo DECIMAL(6,2),nit INT(10))engine=memory;
  23.    OPEN cursor_suma;
  24.    FETCH cursor_suma INTO total_registro;
  25.    OPEN crs_num_fact;
  26.    OPEN crs_cod_prod;  -- abre el LOOP
  27.     crs_cod_fact_loop:LOOP
  28.          WHILE acumulador <= total_registro DO-- LOOP donde recorrera las fechas que se filtraron
  29.                 FETCH crs_num_fact INTO cod_factura,fecha_factura;
  30.                 OPEN crs_cod_prod;
  31.                  SET acumulador = acumulador + 1; -- contador de uno en uno.
  32.                 crs_cod_pro_loop:LOOP --
  33.                         FETCH crs_cod_prod INTO cod_producto, nombre_producto,costo,nit;
  34.                         INSERT INTO informe VALUES('',fecha_factura,cod_producto,nombre_producto,costo,nit);
  35.                         IF v_last_row_fetched = 1 THEN
  36.                             Leave crs_num_fact_loop;
  37.                         END IF;
  38.                  END LOOP crs_cod_pro_loop;
  39.          END WHILE;
  40.     CLOSE csr_num_fact;
  41.     CLOSE csr_cod_prod;
  42.   DROP TABLE IF EXISTS informe;
  43.  END;//
  44. delimiter ;

Última edición por gnzsoloyo; 23/07/2014 a las 17:26
  #6 (permalink)  
Antiguo 23/07/2014, 18:12
Avatar de gnzsoloyo
Moderador criollo
 
Fecha de Ingreso: noviembre-2007
Ubicación: Actualmente en Buenos Aires (el enemigo ancestral)
Mensajes: 23.324
Antigüedad: 17 años
Puntos: 2658
Respuesta: problemilla Procedure

Una pregunta, antes de avanzar en el caso.
En el cursor "crs_cod_prod " tienes esta query:

Código MySQL:
Ver original
  1. SELECT ord_cod_zap, pro_nomb, cal_cost, pro_nit
  2. FROM ord_compra oc, Producto p, Calzado c
  3. WHERE ord_cod_fact = codigo_factura;

¿Cómo se relaciona la tabla "Calzado"? Porque como la tienes puesta, estás generando un producto cartesiano, lo que daría resultados completamente falsos...

Por otro lado, hacer un CURSOR para ejecutar esto:
Código MySQL:
Ver original
  1.     FROM Factura
  2.     WHERE fact_fecha >= fecha_inicio
  3.         AND fact_fecha <= fecha_final

no tiene ningún sentido. Es un desperdicio de recursos de servidor

Es muchísimo más simple hacer:
Código MySQL:
Ver original
  1. FROM Factura
  2. WHERE fact_fecha >= fecha_inicio
  3.         AND fact_fecha <= fecha_final
  4. INTO total_registro;

Adicionalmente, veo que ni te has molestado en leer el link que te pasé, ya que estás creando el HANDLER antes de los cursores.
Si te tomas el trabajo de leer el link verás este detalle:
Cita:
2. No se pueden definir variables en el SP con posterioridad al CURSOR, ni se puede definir un HANDLER antes del CURSOR.
Esa nota está tomada del Manual de referencia de MySQL, donde se expresa:
Cita:
Los cursores deben declararse antes de declarar los handlers, y las variables y condiciones deben declararse antes de declarar cursores o handlers.
Por favor, cuando se te pase un link, léelo. Lo ponemos para que leas alguna referencia que viene al tema.

Yo sigo analizando el SP porque creo que hay un modo más simple de lograr lo que pretendes.
__________________
¿A quién le enseñan sus aciertos?, si yo aprendo de mis errores constantemente...
"El problema es la interfase silla-teclado." (Gillermo Luque)

Última edición por gnzsoloyo; 23/07/2014 a las 18:19
  #7 (permalink)  
Antiguo 23/07/2014, 21:29
 
Fecha de Ingreso: febrero-2013
Ubicación: Cali Colombia
Mensajes: 118
Antigüedad: 11 años, 8 meses
Puntos: 0
Respuesta: problemilla Procedure

Saludos, gracias por las recomendaciones ya hize las correcciones y por lo menos no muestra errores en "compilacion" en el workBench.

Resulta que si es verdad, arroja el query que tengo en el joins con tres tablas unos
resultados aunque no falsos si repetitivos.

Logre quitar una variable acumuladora y el codigo quedo asi. Trato de corregir el query para mirar resultados y sigo con el otro procedimiento que me falta el cual consiste en que la tabla en memoria almacene los query y realize informes muchos mas faciles.

Código SQL:
Ver original
  1. delimiter //
  2. CREATE PROCEDURE devuelve_informe_venta(IN fecha_inicio DATE, IN fecha_final DATE)
  3.  
  4. BEGIN    
  5.    DECLARE fecha_factura DATE;
  6.    DECLARE codigo_factura INT DEFAULT 0;
  7.    DECLARE cod_producto INT DEFAULT 0;
  8.    DECLARE costo DOUBLE DEFAULT 0.0;
  9.    DECLARE total_registro INT DEFAULT 0;
  10.    DECLARE acumulador INT DEFAULT 0;
  11.    DECLARE nombre_producto VARCHAR(20);
  12.    DECLARE nit INT DEFAULT 0;
  13.   DECLARE v_control bool DEFAULT FALSE;
  14.  
  15.    DECLARE crs_cod_fact CURSOR FOR SELECT fact_cod_fac,fact_fecha FROM Factura
  16.                         WHERE fact_fecha >= fecha_inicio AND fact_fecha <= fecha_final;
  17.    
  18.    DECLARE crs_cod_prod CURSOR FOR SELECT ord_cod_zap,pro_nomb,cal_cost,pro_nit FROM ord_compra oc, Producto p,Calzado c
  19.                       WHERE ord_cod_fact = codigo_factura;
  20.    DECLARE CONTINUE HANDLER FOR NOT FOUND SET  v_control = TRUE;
  21.    CREATE TEMPORARY TABLE informe (num INT(5) AUTO_INCREMENT PRIMARY KEY,fecha DATE,cod_cal INT(10),nomb_cal VARCHAR(20),costo DECIMAL(6,2),nit INT(10))engine=memory;
  22.  
  23.     crs_cod_fact_loop:loop
  24.         fetch crs_cod_fact INTO codigo_factura,fecha_factura;
  25.           IF v_control THEN
  26.               leave crs_cod_fact_loop;
  27.               close crs_cod_fact;
  28.           END IF;        
  29.          crs_cod_pro_loop:LOOP -- segundo LOOP donde realiza el la busqueda de nombre de productos, codigo y costo.
  30.             OPEN crs_cod_prod;
  31.             fetch crs_cod_prod INTO cod_producto, nombre_producto,costo,nit;
  32.             INSERT INTO informe VALUES('',fecha_factura,cod_producto,nombre_producto,costo,nit);
  33.             IF v_control THEN  
  34.                 close crs_cod_prod;
  35.                 leave crs_cod_pro_loop;
  36.             END IF;
  37.          END LOOP crs_cod_pro_loop;
  38.     END LOOP crs_cod_fact_loop;
  39.   DROP TABLE IF EXISTS informe;
  40.  END;//
  41. delimiter ;

Etiquetas: function, handler, procedure
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 10:30.