miércoles, 2 de julio de 2008

Pienso-pienso: subrutina que recorre la misma tabla que el código principal

Siguiendo con la línea de los pienso-pienso de Enrique, acá planteo uno:

Tengo el siguiente código
for each
where [condiciones]
do 'Promedio'
msg('Id = ' + str(id) + '; valor = ' + str(valor))
endfor

Sub 'Promedio'
&cantidad = 0
&total = 0
for each
where
[condiciones] // mismas condiciones que arriba
if valor <> 0
&cantidad += 1
&total += valor
endif
endfor
if &cantidad <> 0
&promedio = &total / &cantidad
endif
EndSub
Si en la tabla tengo los valores (id, valor) = {(1, 10), (2, 20), (3, 30)}, ¿qué imprimen los msg del programa?

a)
id = 1, valor = 10
id = 2, valor = 20
id = 3, valor = 30

b)
id = 1, valor = 10
id = 2, valor = 10
id = 3, valor = 10

c)
id = 1, valor = 20
id = 2, valor = 20
id = 3, valor = 20

d)
id = 1, valor = 30
id = 2, valor = 30
id = 3, valor = 30

Justificar la respuesta :)

4 comentarios:

  1. La respuesta correcta es la d). Exactamente este tema me ha traído muchos dolores de cabezas, para mí esto no debería pasar y debería mostrar la opcion a).

    Nunca me puse a investigar el código generado pero supongo que sucede algo como esto:

    Al entrar al primer for each, se ejecuta una consulta SQL y se guardan en las variables &ID y &Valor los valores correspondientes a ese registro.

    Luego en el sub se ejecuta una nueva consulta SQL que devuelve todos los registros y se recorren guardando "temporalmente" estos valores en las variables &ID (esto no es correcto para este ejemplo) y &Valor. por lo cual, al salir del for each el valor de la variable &Valor es siempre el del último registro, o sea "30". por eso siempre imprime "30".

    El motivo por el cual la variable ID no pierde su valor es que no interviene en el segundo "For Each" y seguramente no es usado durante la consulta.

    Espero haberme explicado correctamente.

    Saludos.

    ResponderEliminar
  2. Diego, esa es exactamente la respuesta. También es correcta la justificación.

    En el código generado (en Java), para cada atributo define una variable cuyo nombre se compone de un prefijo, el id del campo y el nombre del atributo. El tema es que en los dos for each usa la misma variable para el campo Valor, por lo que al volver de la subrutina el "atributo" (que en realidad es la variable interna) tiene un valor que no es el esperado.

    La solución en estos casos suele ser hacer un procedure en vez de la subrutina, que ahí no tenés el problema.

    En este ejemplo en particular alcanza con poner el "if valor <> 0" como un where, para que genere el select optimizado con count() y sum(), que ahí no usa la variable para el campo Valor.

    ResponderEliminar
  3. ¿ Esto es un BUG de Genexus segun tu punto de vista ?. Según el mio si.

    Saludos.

    ResponderEliminar
  4. No se si es un bug, lo que sí está claro es que no es lo que uno esperaría a primera vista.

    A lo mejor como esto es algo que siempre funcionó así, ya lo tengo incorporado y no me resulta tan raro. Si no fuera raro no lo habría puesto en el blog :)

    En todo caso, lo que hay que hacer es aprender como funciona GX por abajo, para saber por que un programa así hace lo que hace.

    ResponderEliminar