PiensoPienso: Atributo formula : Conviene usar procedure o count?

Tengo una Transacción con la estructura

*CabezalClave
 CabezalDescripcion
 CantidadDeLineas = Formula que cuenta la cantidad de líneas
  (
    *LineaClave
     LineaDescripcion
   )  


Se tiene un procedure CuentoLineas que hace
parm(in:&CabezalClave,out:&CantidadDeLineas);

&CantidadDeLineas=0
for each
   where CabezalClave=&CabezalClave
     &CantidadDeLineas += 1
endfor

Debo hacer un programa que liste todos los cabezales que tengan mas de 3 lineas y poner cuantas lineas tiene.

CabezalClave , #LineasDelCabezal.

Cual de estas opciones utilizaría ? Justifique la respuesta.

Opcion 1) VARIABLE LLAMANDO A PROCEDURE

for each
     &CantidadDeLineas = CuentoLineas(CabezalClave)
     if &CantidadDeLineas > 3 
          Msg(CabezalClave + "," + &CantidadDeLineas)
     endif
endfor

Opcion 2)  ATRIBUTO FORMULA CON PROCEDURE

Defino un atributo fórmula

*CabezalClave
 CabezalDescripcion
 CantidadDeLineas = CuentoLineas(CabezalClave)
  (
    *LineaClave
     LineaDescripcion
   )  


y el código
for each
   where CantidadDeLineas > 3
       Msg(CabezalClave + "," + CantidadDeLineas)
endfor

Opcion 3) ATRIBUTO FORMULA COUNT()
*CabezalClave
 CabezalDescripcion
 CantidadDeLineas = COUNT(LineaDescripcion)
  (
    *LineaClave
     LineaDescripcion
   )  


y el código
for each
   where CantidadDeLineas > 3
       Msg(CabezalClave + "," + CantidadDeLineas)
endfor


===== RESULTADOS ===
En primer lugar, hay que hacer notar, que no hay una respuesta correcta, sino diferentes formas de solucionar un problema. En algunos casos es mas util una y en otros casos se puede usar otra.

Para medir la performance de estas soluciones, hice una prueba usando C# con SQL Servver y tablas  con 65.536 registros en Cabezal y 3.294.009 Lineas.

Hay 63026 registros de Cabezal, que tienen mas de 3 lineas.

 Hice el procedure.

msg('Opcion 1) VARIABLE LLAMANDO A PROCEDURE',STATUS)
&CantLineas=0
&Inicio = now()
for each
     &CantidadDeLineas = CuentoLineas(CabezalClave)
     if &CantidadDeLineas > 3 
&CantLineas += 1
     endif
endfor
Msg('Cantidad de cabezales con mas de 3 lineas: ' + &CantLineas.ToString(),status)
&Fin = now()
&Diff = &Fin.Difference(&Inicio)
msg('Demoro: ' + &Diff.ToString() + ' Inicio : ' + &Inicio.ToString() + ' Fin : ' + &Fin.ToString(),status)
msg(newline(),status)

MSG('Opcion 2)  ATRIBUTO FORMULA CON PROCEDURE',STATUS)
&CantLineas=0
&Inicio = now()
for each
   where CantLineasClaveProcedure > 3
  &CantLineas += 1
endfor
Msg('Cantidad de cabezales con mas de 3 lineas: ' + &CantLineas.ToString(),status)
&Fin = now()
&Diff = &Fin.Difference(&Inicio)
msg('Demoro: ' + &Diff.ToString() + ' Inicio : ' + &Inicio.ToString() + ' Fin : ' + &Fin.ToString(),status)
msg(newline(),status)


MSG('Opcion 3) ATRIBUTO FORMULA COUNT()',STATUS)
&CantLineas=0
&Inicio = now()
for each
   where CantLineasClaveCount > 3
  &CantLineas += 1
endfor
Msg('Cantidad de cabezales con mas de 3 lineas: ' + &CantLineas.ToString(),status)
&Fin = now()
&Diff = &Fin.Difference(&Inicio)
msg('Demoro: ' + &Diff.ToString() + ' Inicio : ' + &Inicio.ToString() + ' Fin : '+ &Fin.ToString(),status)
msg(newline(),status)

y el resultado fue:

Opcion 1) VARIABLE LLAMANDO A PROCEDURE
Cantidad de cabezales con mas de 3 lineas:    63026
Demoro:             12.321 Inicio : 02/08/19 02:14:37.572 PM Fin : 02/08/19 02:14:49.893 PM

Opcion 2)  ATRIBUTO FORMULA CON PROCEDURE
Cantidad de cabezales con mas de 3 lineas:    63026
Demoro:             13.770 Inicio : 02/08/19 02:14:49.904 PM Fin : 02/08/19 02:15:03.674 PM

Opcion 3) ATRIBUTO FORMULA COUNT()
Cantidad de cabezales con mas de 3 lineas:    63026
Demoro:              0.359 Inicio : 02/08/19 02:15:03.674 PM Fin : 02/08/19 02:15:04.033 PM

La opción 3) es mucho mas rápida que las anteriores, que tienen una performance bastante similar..

En cuanto a claridad de código y facilidad  en el mantenimiento, lo mas adecuado es tambien la opcion 3) pues  encapsula mejor el conocimiento en una formula reutilizable.

Por lo tanto, en este caso, parece mas adecuado el uso de formulas "nativas", y conviene utilizarlas. 
      


Comentarios

  1. Sin hacer trampa, o sea, ir a escribir el codigo en GX y chusmear lo generado, me inclino por la opcion numero 3. Justifique? Bueno, supongo que GX generara la sentencia SQL con el HAVING COUNT(*) correspondiente. Ademas, las llamadas a procedimientos tienen el lastre de la creacion del proceso en si mismo. Si se ejecuta 1 sola vez puede que no se note, pero si se ejecuta repetidas veces se evidencia.

    ResponderBorrar
    Respuestas
    1. Leandro, vale hacer trampa.

      Doy un poco de tiempo para dar mi opinión, por si alguien mas quiere comentarlo.
      Gracias por leer y comentar.

      Enrique

      Borrar
  2. La verdad, habría que mirar como lo generar GX (que fue cambiando con el tiempo) la opcion 1 igual seria siempre la menos performante en principio (recorre todo. y pregunta x cant > 3 )
    ... y la 3 ) si lo resuelve en la BD seria lo ideal (que los tiempos son abismalmente menores )
    He hecho... para cargar unas estadisticas.. Sum y count directos en la base con sentencia SQL y recupero. y cambia totalmente ( de 20 min a 1 Min !)

    ResponderBorrar
    Respuestas
    1. Esteban, gracias por el comentario.
      Es verdad que ha evolucioando muchisimo la forma en que GeneXus genera estos programas.
      Como bien lo dices, en este caso (y para SQL Server) la diferencia entre las diferentes opciones es bien notoria, siendo la 3) la mas rapida por lejos.

      Borrar
  3. Enrique mirando lo que quieres analizar, me surge una duda y es que donde estoy. Llevan un programa de muchos años (Algo asi como tu Lucia), tienen transacciones en algunos campos llamando UDP que invocan otros PRC, Insertan, Validan considero que pierden el foco. Y estas serie de ejecuciones deberian llamarse desde las Rules. A tu experiencia que seria lo conveniente. Gracias.

    ResponderBorrar
  4. Gustavo:
    No hay una solucion universal, que sea "la correcta" para todas las situaciones.
    En general, antes se hacian muchos atributos formulas con UDP pues habia algunas carencias en el modelado de datos, que se han ido incorporando a Genexus. Tambien habia varios casos en que los subtipos navegaban en forma incorrecta que se han solucionado.

    En general, los sistemas mas nuevos, tienen menos formulas udp que los mas viejos y es una cosa buena.

    En cuanto a tu caso, supongo que tu duda es si conviene definir un atributo como formula para hacer alguna validacion o si conviene ponerlo solo como regla en la transaccion.

    En general, trato de analizar si el atributo va a ser util en algun otro objeto de la KB y si tiene algun significado fuera de la transaccion. Si lo tiene, lo defino como atributo formula. Si no lo tiene, trato de usarlo solo en las reglas.

    ResponderBorrar

Publicar un comentario

1) Lee el post
2) Poné tu opinión sobre el mismo.
Todos los comentarios serán leidos y la mayoría son publicados.

Entradas más populares de este blog

Aplicación monolítica o distribuida?

La nefasta influencia del golero de Cacho Bochinche en el fútbol uruguayo

Funcionalidades de GeneXus que vale la pena conocer: DATE Constants.