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

Procedimiento almacenado con ciclos anidados

Estas en el tema de Procedimiento almacenado con ciclos anidados en el foro de PostgreSQL en Foros del Web. TENGO un procedimiento almacenado con ciclos o loops anidados, y este no retorna ningun valor, el procediento me lo deja crear sin problema pero cuando ...
  #1 (permalink)  
Antiguo 11/11/2009, 11:01
Avatar de webness  
Fecha de Ingreso: enero-2009
Ubicación: BOGOTA
Mensajes: 312
Antigüedad: 15 años, 3 meses
Puntos: 5
Pregunta Procedimiento almacenado con ciclos anidados

TENGO un procedimiento almacenado con ciclos o loops anidados, y este no retorna ningun valor, el procediento me lo deja crear sin problema pero cuando lo ejecuto no me genera un error.

El objetivo de este es actualizar los datos de 2 columnas de una tabla si y solo si hay datos para esa columna.

estoy viendo en: http://www.postgresql.org/docs/8.0/s...tructures.html, en la seccion 35.7.4. Looping Through Query Results un ejemplo para guiarme.

Mi codigo:
Código PL/PGSQL:
Ver original
  1. CREATE OR REPLACE FUNCTION p_update_locales_in_count() RETURNS boolean AS $BODY$
  2. DECLARE
  3.     query_count         RECORD;
  4.     query_has_locales   RECORD;
  5.     query_locale        RECORD;
  6. BEGIN
  7.     FOR query_count IN SELECT neighborhood_code,city_code FROM  conteo_manzanas_barrio_co LOOP
  8.    
  9.         FOR query_has_locales IN SELECT has_locales FROM sm_city WHERE pk_city = query_count.city_code.city_code LOOP
  10.        
  11.             IF query_locales.has_locales THEN
  12.                 SELECT  pk_locale,name FROM sm_locale WHERE fk_pk_city = query_count.city_code;
  13.                
  14.                 UPDATE  conteo_manzanas_barrio_co
  15.                 SET     locale_code = pk_locale, locale_name = name
  16.                 WHERE   neighborhood_code = query_count.neighborhood_code;
  17.             END IF;
  18.        
  19.         END LOOP;
  20.        
  21.     END LOOP;
  22.  
  23.     RETURNS TRUE;  
  24. END $BODY$
  25. LANGUAGE plpgsql;

Y EL ERROR QUE BOTA ES:

Código ERROR:
Ver original
  1. [COLOR="Red"][SIZE="3"][FONT="Comic Sans MS"]
  2. Error de SQL:
  3.  
  4. ERROR:  schema "query_count" does not exist
  5. CONTEXT:  SQL statement " SELECT has_locales FROM sm_city WHERE pk_city = query_count.city_code.city_code"
  6. PL/pgSQL function "p_update_locales_in_count" line 8 at for over select rows
  7.  
  8. En la declaración:
  9. SELECT * FROM p_update_locales_in_count()[/FONT][/SIZE][/COLOR]
¿EN SINTESIS QUE TIENE DE MALO MI PROCEDIMIENTO, ES ESA LA FORMA CORRECTA DE LLAMARLO?
  #2 (permalink)  
Antiguo 11/11/2009, 12:31
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Procedimiento almacenado con ciclos anidados

hay varias cosas en las que veo inconsistencias.

Cita:
IF query_locales.has_locales THEN
Esta comparando esto con que?
no sería:
IF query_has_locales.has_locales = 'algo' THEN?
dentro del if hace esta consulta
Cita:
SELECT pk_locale,name FROM sm_locale WHERE fk_pk_city = query_count.city_code;
que fin tiene esa consulta? no veo que se salven los valores retornados por la consulta para ser usados.

Donde se usa este record declarado? query_locale

Describe un poco que tratas de hacer. Tal vez haya una forma mas optima de obtener los mismos resultados.

saludos
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
  #3 (permalink)  
Antiguo 11/11/2009, 12:36
Avatar de webness  
Fecha de Ingreso: enero-2009
Ubicación: BOGOTA
Mensajes: 312
Antigüedad: 15 años, 3 meses
Puntos: 5
Exclamación Respuesta: Procedimiento almacenado con ciclos anidados

Ya corregi el procedimiento y no ya no existe el problema anterior. quedo asi:

Código SQL:
Ver original
  1. CREATE OR REPLACE FUNCTION p_update_locales_in_count() RETURNS BOOLEAN AS $BODY$
  2. DECLARE
  3.     query_count         RECORD;
  4.     query_has_locales   RECORD;
  5. BEGIN
  6.     FOR query_count IN SELECT neighborhood_code,city_code FROM  conteo_manzanas_barrio_co LOOP
  7.    
  8.         FOR query_has_locales IN SELECT has_locales FROM sm_city WHERE pk_city = query_count.city_code LOOP
  9.        
  10.             IF query_has_locales.has_locales THEN
  11.                 SELECT  pk_locale,name FROM sm_locale WHERE fk_pk_city = query_count.city_code;
  12.                
  13.                 UPDATE  conteo_manzanas_barrio_co
  14.                 SET     locale_code = pk_locale, locale_name = name
  15.                 WHERE   neighborhood_code = query_count.neighborhood_code;
  16.             END IF;
  17.        
  18.         END LOOP;
  19.        
  20.     END LOOP;
  21.  
  22.     RETURNS TRUE;  
  23. END $BODY$
  24. LANGUAGE plpgsql;


AHORA LO QUE NO ES COMO LLAMARLO PUES LO ESTOY LLAMANDO ASI:

SELECT * FROM p_update_locales_in_count()

Y ME ARroja este error:

Código ERROR:
Ver original
  1. Error de SQL:
  2.  
  3. ERROR:  SELECT query has no destination for result data
  4. HINT:  If you want to discard the results, use PERFORM instead.
  5. CONTEXT:  PL/pgSQL function "p_update_locales_in_count" line 10 at SQL statement
  6.  
  7. En la declaración:
  8. SELECT * FROM p_update_locales_in_count()

como debo llamarlo o que debo cambiarle?
  #4 (permalink)  
Antiguo 11/11/2009, 12:41
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Procedimiento almacenado con ciclos anidados

SELECT * FROM p_update_locales_in_count()

esta es la forma de llamar procedimientos que tienen multiples salidas (por así decirlo)

Tu procedimiento solo retorna un valor true o false entonces con llamarlo
select p_update_locales_in_count() bastará.

En el retorno haces uso de la palabra returns, la sintaxis correcta es return

saludos
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
  #5 (permalink)  
Antiguo 11/11/2009, 12:44
Avatar de webness  
Fecha de Ingreso: enero-2009
Ubicación: BOGOTA
Mensajes: 312
Antigüedad: 15 años, 3 meses
Puntos: 5
Respuesta: Procedimiento almacenado con ciclos anidados

Me salio este error

Error de SQL:

ERROR: SELECT query has no destination for result data
HINT: If you want to discard the results, use PERFORM instead.
CONTEXT: PL/pgSQL function "p_update_locales_in_count" line 10 at SQL statement

En la declaración:
SELECT p_update_locales_in_count()
  #6 (permalink)  
Antiguo 11/11/2009, 12:54
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Procedimiento almacenado con ciclos anidados

Cita:
hay varias cosas en las que veo inconsistencias.

Cita:
IF query_locales.has_locales THEN
Esta comparando esto con que?
no sería:
IF query_has_locales.has_locales = 'algo' THEN?
dentro del if hace esta consulta
Cita:
SELECT pk_locale,name FROM sm_locale WHERE fk_pk_city = query_count.city_code;
que fin tiene esa consulta? no veo que se salven los valores retornados por la consulta para ser usados.

Donde se usa este record declarado? query_locale

Describe un poco que tratas de hacer. Tal vez haya una forma mas optima de obtener los mismos resultados.

saludos
__________________
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
  #7 (permalink)  
Antiguo 11/11/2009, 13:07
Avatar de webness  
Fecha de Ingreso: enero-2009
Ubicación: BOGOTA
Mensajes: 312
Antigüedad: 15 años, 3 meses
Puntos: 5
Respuesta: Procedimiento almacenado con ciclos anidados

IF query_locales.has_locales THEN -- EL VALOR CONTENIDO en query_locales.has_locales ES UN BOOLEANO, SUPONGO QUE ASI ES SUFICIENTE O NO?


SELECT pk_locale,name FROM sm_locale WHERE fk_pk_city = query_count.city_code;

lo que quiero es obtener los valores de estos dos campos para despues poder hacer una actualizacion con estos dos valores

UPDATE conteo_manzanas_barrio_co
SET locale_code = pk_locale, locale_name = name
WHERE neighborhood_code = query_count.neighborhood_code;
  #8 (permalink)  
Antiguo 11/11/2009, 13:13
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Procedimiento almacenado con ciclos anidados

ok.

pero la asignación está mal hecha.
Declarate otro record.
Código sql:
Ver original
  1. record_prueba record;
  2.  
  3. --Y en la consulta haces asi:
  4. SELECT INTO record_prueba pk_locale,name FROM sm_locale WHERE fk_pk_city = query_count.city_code;
  5.  
  6. UPDATE  conteo_manzanas_barrio_co
  7.                 SET     locale_code = record_prueba.pk_locale, locale_name = record_prueba.name
  8.                 WHERE   neighborhood_code = query_count.neighborhood_code;
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
  #9 (permalink)  
Antiguo 11/11/2009, 13:29
Avatar de webness  
Fecha de Ingreso: enero-2009
Ubicación: BOGOTA
Mensajes: 312
Antigüedad: 15 años, 3 meses
Puntos: 5
Respuesta: Procedimiento almacenado con ciclos anidados

Ya se ejecuto el procedimiento, pero no realizo combio alguno en la tabla que se intento modificar con el update
  #10 (permalink)  
Antiguo 11/11/2009, 13:33
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Procedimiento almacenado con ciclos anidados

asegurate que esté entrando al if.
crea una variable al principio de la función con valor de cero.
Dentro del if cambiale el valor a 1 y retornala.

fijate si entra o no al if.

saludos
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
  #11 (permalink)  
Antiguo 11/11/2009, 13:41
Avatar de webness  
Fecha de Ingreso: enero-2009
Ubicación: BOGOTA
Mensajes: 312
Antigüedad: 15 años, 3 meses
Puntos: 5
Respuesta: Procedimiento almacenado con ciclos anidados

bUENO, POR AHORA te cuento que pase por alto que en el utlimo query no se retorna un solo valor sino muchos, entonces inclui un tercer ciclo asi:

Código SQL:
Ver original
  1. CREATE OR REPLACE FUNCTION p_update_locales_in_count() RETURNS BOOLEAN AS $BODY$
  2. DECLARE
  3.     query_count         RECORD;
  4.     query_has_locales   RECORD;
  5.     real_data           RECORD;
  6. BEGIN
  7.     FOR query_count IN SELECT neighborhood_code,city_code FROM  conteo_manzanas_barrio_co LOOP
  8.    
  9.         FOR query_has_locales IN SELECT has_locales FROM sm_city WHERE pk_city = query_count.city_code LOOP
  10.        
  11.             IF query_has_locales.has_locales THEN
  12.                 FOR real_data IN SELECT pk_locale,name FROM sm_locale WHERE fk_pk_city = query_count.city_code LOOP
  13.                
  14.                     UPDATE  conteo_manzanas_barrio_co
  15.                     SET     locale_code = real_data.pk_locale, locale_name = real_data.name
  16.                     WHERE   neighborhood_code = query_count.neighborhood_code;
  17.                 END LOOP;  
  18.             END IF;
  19.        
  20.         END LOOP;
  21.        
  22.     END LOOP;
  23.  
  24.     RETURNS TRUE;  
  25. END $BODY$
  26. LANGUAGE plpgsql;

SI NO FUNCIONA ASI, ENTONCES PROCEDO CON LO QUE QUE ME ACABASTE DE DECIR..


GRACIAS
  #12 (permalink)  
Antiguo 11/11/2009, 13:57
Avatar de webness  
Fecha de Ingreso: enero-2009
Ubicación: BOGOTA
Mensajes: 312
Antigüedad: 15 años, 3 meses
Puntos: 5
Respuesta: Procedimiento almacenado con ciclos anidados

AL IF SI ENTRA, PERO SUPONGO QUE NO ACTUALIZA LOS DATOS, PORQUE EL PROCESO ES DEMORADO Y SE ME PRESENTA UNA CAIDA DE LA COMUNICACION ENTRE MI CLIENTE QUE ES phpPgAdmin y el server.

Ahoro me pregunto yo

¿El commit de las transacciones de actualizacion se hace solo al final del script?, pregunto porque si se cae la conexion puede ser que no este terminado el scrpt y no haga commit

Le añadi comit por fuera del 3er ciclo. estoy ensayando
  #13 (permalink)  
Antiguo 11/11/2009, 14:05
Avatar de webness  
Fecha de Ingreso: enero-2009
Ubicación: BOGOTA
Mensajes: 312
Antigüedad: 15 años, 3 meses
Puntos: 5
Respuesta: Procedimiento almacenado con ciclos anidados

tampoo funciono asi
  #14 (permalink)  
Antiguo 11/11/2009, 14:06
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Procedimiento almacenado con ciclos anidados

No es necesario.

Una función trae consigo una transacción implícita.
o se ejecuta toda la función o no se ejecuta nada.

ya debes enfocar el problema a aumentar el tiempo de vida de la sesión desde el servidor web para que se pueda ejecutar toda la función.
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
  #15 (permalink)  
Antiguo 11/11/2009, 14:49
Avatar de webness  
Fecha de Ingreso: enero-2009
Ubicación: BOGOTA
Mensajes: 312
Antigüedad: 15 años, 3 meses
Puntos: 5
Respuesta: Procedimiento almacenado con ciclos anidados

Igual tenia el ultimo select mal hecho, muy mal hecho, ahora mi funcion es asi:

Código sql:
Ver original
  1. CREATE OR REPLACE FUNCTION p_update_locales_in_count() RETURNS INTEGER AS $BODY$
  2. DECLARE
  3.     query_count         RECORD;
  4.     query_has_locales   RECORD;
  5.     real_data           RECORD;
  6. BEGIN
  7.     FOR query_count IN SELECT neighborhood_code,city_code FROM  conteo_manzanas_barrio_co LOOP
  8.    
  9.         FOR query_has_locales IN SELECT has_locales FROM sm_city WHERE pk_city = query_count.city_code LOOP
  10.        
  11.             IF query_has_locales.has_locales THEN
  12.                
  13.                 SELECT  INTO real_data  sl.pk_locale,sl.name
  14.                 FROM    sm_locale sl,sm_neighborhood sn,servcon_barrios sb
  15.                 WHERE   sn.pk_neighborhood = query_count.neighborhood_code AND
  16.                         sl.fk_pk_city = query_count.city_code AND
  17.                         sb.cod_localidad = sl.locale_code AND
  18.                         sb.cod_barrio = sn.neighborhood_code AND
  19.                         sl.name = sb.nom_localidad LOOP
  20.                
  21.                 UPDATE  conteo_manzanas_barrio_co
  22.                 SET     locale_code = real_data.pk_locale, locale_name = real_data.name
  23.                 WHERE   neighborhood_code = query_count.neighborhood_code;
  24.             END IF;
  25.            
  26.         END LOOP;
  27.        
  28.     END LOOP;
  29.  
  30.     RETURN 0;  
  31. END $BODY$
  32. LANGUAGE plpgsql;

y al ejecutar el procedimiento me da arroja este error

ERROR: record "real_data" is not assigned yet
DETAIL: The tuple structure of a not-yet-assigned record is indeterminate.
CONTEXT: PL/pgSQL function "p_update_locales_in_count" line 12 at select into variables
En la declaración:
select p_update_locales_in_count()
  #16 (permalink)  
Antiguo 11/11/2009, 14:59
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Procedimiento almacenado con ciclos anidados

webness definitivamente el que entiende bien el problema sos vos.
Yo te recomiendo algo...
create unas tablas de prueba e inserta algunos registros.
Prueba en una nueva función parte por parte de lo que tienes hecho. Mira primero en un solo for y una sentencia de actualización si los valores por los que pasa el ciclo lo hace correctamente.
Cuando te cerciores de que todo anda bien, añade otro for y haz el respectivo seguimiento de la función. Tomandolo parte por parte, entenderás mejor la panorámica del asunto y podrás identificar fácilmente los errores.

Los errores en plpgsql son muy engañosos y no siempre detectan correctamente el error.
Cita:
ERROR: record "real_data" is not assigned yet
suena como si no se hubiera ejecutado el select into.
Ahora bien, cuando haces una consulta de varias tablas, lo mejor es hacer uso de sentencias join y no anidación de claves en el where. También es muy recomendable hacer uso de los alias en los campos precedidos por el nombre de la tabla. Fijate que tu campo en la consulta es sl.pk_locale y desde el record lo llamas real_data.pk_locale. Haciendo uso de alias vas a la fija.
sl.pk_locale as pkl y desde el record se llama real_data.pkl.

saludos
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
  #17 (permalink)  
Antiguo 11/11/2009, 15:30
Avatar de webness  
Fecha de Ingreso: enero-2009
Ubicación: BOGOTA
Mensajes: 312
Antigüedad: 15 años, 3 meses
Puntos: 5
Respuesta: Procedimiento almacenado con ciclos anidados

Logre suprimir un ciclo, optimizando la primera consulta.

esta primera me devolvia 16800 registros, y por cada uno de estos hacia la segunda consulta.

y por cada registro de esta segunda consulta si cumple con la condicion del IF hacia las dos consultas....

ahora, optimeze el primer query devolviendo solo 8600 registros, que son los que cumplen con la condicion del IF. y ejecuto las 2 consultas
  #18 (permalink)  
Antiguo 12/11/2009, 16:01
Avatar de webness  
Fecha de Ingreso: enero-2009
Ubicación: BOGOTA
Mensajes: 312
Antigüedad: 15 años, 3 meses
Puntos: 5
De acuerdo Respuesta: Procedimiento almacenado con ciclos anidados

despùes de todo, no necesite el procedimiento almacenado.

Resulta ser que mi panorama de trabajo es mas o menos con unas 160 tablas, las cuales manejan mucha redundancia y valores nulos, y se hacen muchas consultas de conteo, que son muy ineficientes, mi labor es optimizar, y para ello empeze a crear tablas nuevas como si fueran unas vistas materializadas de oracle, pero son cimplemente unas tablas que creo fruto de un query. como son tantas pues no conoxco todo el sistema.

al fin de cuentas mi solucion fue, crear otra tabla, y con ella actualizar los valores de otras 3 tablas y ya, me vali del uso de vistas.

de todos modos estoy en proceso de aprendizaje con el EL PL/PGSQL, y mi procedimiento quedo asi por si a agluein le sirve:

Código sql:
Ver original
  1. CREATE OR REPLACE FUNCTION p_update_locales_in_count() RETURNS INTEGER AS $BODY$
  2. DECLARE
  3.     query_count         RECORD;
  4.     query_has_locales   RECORD;
  5.     real_data           RECORD;
  6. BEGIN
  7.    
  8.     FOR query_count IN  SELECT  co.neighborhood_code,co.city_code
  9.                         FROM    conteo_manzanas_barrio_co co,sm_city ci
  10.                         WHERE   co.city_code = ci.pk_city AND has_locales = TRUE
  11.                         LIMIT 1 OFFSET 0 LOOP
  12.        
  13.         SELECT  INTO real_data  sl.pk_locale,sl.name
  14.         FROM    sm_locale sl,sm_neighborhood sn,servcon_barrios sb
  15.         WHERE   sn.pk_neighborhood = query_count.neighborhood_code AND
  16.                 sl.fk_pk_city = query_count.city_code AND
  17.                 sb.cod_localidad = sl.locale_code AND
  18.                 sb.cod_barrio = sn.neighborhood_code AND
  19.                 sl.name = sb.nom_localidad;
  20.                
  21.         UPDATE  conteo_manzanas_barrio_co
  22.         SET     locale_code = real_data.pk_locale, locale_name = real_data.name
  23.         WHERE   neighborhood_code = query_count.neighborhood_code;
  24.        
  25.     END LOOP;
  26.  
  27.     RETURN 0;  
  28. END $BODY$
  29. LANGUAGE plpgsql;
  #19 (permalink)  
Antiguo 12/11/2009, 16:05
Avatar de huesos52
Colaborador
 
Fecha de Ingreso: febrero-2009
Ubicación: Manizales - Colombia
Mensajes: 5.980
Antigüedad: 15 años, 2 meses
Puntos: 360
Respuesta: Procedimiento almacenado con ciclos anidados

gracias por compartir la solución webness
__________________
Without data, You are another person with an opinion.
W. Edwads Deming
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 01:04.