Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Conexión con bases de datos (https://www.clubdelphi.com/foros/forumdisplay.php?f=2)
-   -   Un Insert muy lento......... (https://www.clubdelphi.com/foros/showthread.php?t=21764)

sitrico 26-05-2005 23:23:15

Un Insert muy lento.........
 
Estoy trabajando en los reportes de una aplicación y opté por usar un procedimiento para generar una tabla local temporal (BDE) para almacenar las lineas del reporte y luego generar la salida usando un TQuickRep.

Todo funciona razonablemente bien pero el rendimiento es patetico, haciendo diversas pruebas logre detectar que si quito la linea de "ExecSQL" que corresponde al insert de los datos en la tabla temporal el rendimiento cambia drasticamente (desgraciadamente no se genera el reporte :rolleyes: ) pero agregar unos 400 registros en el archivo temporal toma hasta 20seg (si quito la linea "ExecSQL" todo el proceso dura menos de 1 seg) por lo que asumo que mi problema de rendimiento está directamente asociado al insert

Este es el código: (fDataMod.Q1 es un tQuery conectado a un TDatabase local).


Código Delphi [-]
With fDataMod.Q1 do
   Begin
   Sql.Clear;
   Sql.Add('Insert into '+CuotedStr(FName));
   Sql.Add('(CodMay, CodAux, Descrip, Ref, Concep, NumComp, FchComp,');
   Sql.Add(' M1, M2, M3, M4, LinTop, Bold, IdDetalle ) Values');
   Sql.Add('(:CodMay, :CodAux, :Descrip, :Ref, :Concep, :NumComp, :FchComp,');
   Sql.Add(' :M1, :M2, :M3, :M4, :LinTop, :Bold, :IdDetalle)');
   ParamByName('CodMay').AsString := Datos.CMay;
   ParamByName('CodAux').AsString := Datos.CAux;
   ParamByName('Descrip').AsString := Datos.Desc;
   ParamByName('Ref').AsString := Datos.Ref;
   ParamByName('Concep').AsString := Datos.Conc;
   ParamByName('NumComp').AsString := Datos.NComp;
   ParamByName('FchComp').AsDate := Datos.FComp;
   ParamByName('M1').AsCurrency := Datos.M1;
   ParamByName('M2').AsCurrency := Datos.M2;
   ParamByName('M3').AsCurrency := Datos.M3;
   ParamByName('M4').AsCurrency := Datos.M4;
   ParamByName('LinTop').AsInteger := Datos.LTop;
   ParamByName('Bold').AsBoolean := Datos.Bold;
   ParamByName('IdDetalle').AsInteger := Datos.IdDet;
   ExecSQL;
   End;
End;

¿ El uso de Parametros afecta (mucho) el rendimiento ?
¿ Que puedo hacer para acelerar el proceso de inserción ?

Gracias

roman 26-05-2005 23:34:31

Curiosa manera de generar un reporte. ¿Por qué en lugar de crear una tabla temporal no simplemente haces una consulta SELECT de los datos que necesites y con ella alimentas al QuickReport?

// Saludos

sitrico 27-05-2005 00:06:25

Sabia que me iban a preguntar por eso, :D

La razón principal para generar el temporal es que entre otras características de la aplicación me pidieron que fuera capaz de generar los reporte tanto en impresoras de tinta y laser como en matriz de puntos (en formato plano) y la solución que encontré fue precisamente llenar un archivo temporal con los datos del reporte y luego llamar a un tQuickRep para la salida "Windows" y/o a un procedimiento para la la salida en Matriz de Puntos.

También debo aclarar que la mayoria de los reportes son bastante complejos (no son presisamente un simple listado de una o varias tablas) incluyen un monton de líneas adicionales que no están en la tabla, totales por nivel de grupo y otras cosas raras.

Por eso preferí pre-procesar el reporte y después generarlo, y también (aunque parezca mentira) porque me permite usar un código más consistente a la hora de administrar los QuickRep (Solo uso una clase base que heredo para la salida) creando los campos de reporte desde el código en tiempo de ejecución.

Pero, en el fondo tienes razón, normalmente es más fácil generar una consulta e imprimirla directamente.

Mick 27-05-2005 00:48:04

No indicas que tipo de tabla es, pero si es paradox o dbase, es infinitamente mas rapido usar un TTable y asignar los campos directamente.

Otra problema con esa query es que la estas limpiando y recreando continuamente (el Sql.Clear y Sql.Add) por cada registro, esto obliga al engine a parsear la sentencia cientos de veces, lo que aumenta tambien la lentitud, deberias asignar una sola vez la sentencia sql al principio, y despues simplemente solo asignar los parametros en el bucle.

La forma mas rapida es algo como esto:

Código:

// Tabla es un objeto TTable , creado previamente y con los campos
// añadidos en tiempo de diseño: Click boton derecho
// sobre el objeto TTAble -> Edit Fields -> Add Fields

Tabla.Append;
TablaCodMay.AsString := Datos.CMay;
TablaCodAux.AsString := Datos.CAux;
TablaDescrip.AsString := Datos.Desc;
TablaRef.AsString := Datos.Ref;
...
...
Tabla.Post;

Si no quieres usar variables persistentes para los campos, podrias hacer algo como lo siguiente (aunque la funcion FieldByName es mucho mas lenta que simplemente acceder directamente a los campos como en el ejemplo anterior):

Código:

Tabla.Append;
Tabla.FieldByName('CodMay').AsString := Datos.CMay;
Tabla.FieldByName('CodAux').AsString := Datos.CAux;
Tabla.FieldByName('Descrip').AsString := Datos.Desc;
Tabla.FieldByName('Ref').AsString := Datos.Ref;
...
...
Tabla.Post;

Ten en cuenta tambien los indices que haya definidos en la tabla temporal, cuantos mas indices tenga definidos mas lenta es la insercion de registros.

Saludos

jachguate 27-05-2005 00:57:19

Para no cambiar mucho la estructura que ya tenes definida, podes valerte de tun TClientDataset o un TrxMemoryData, definir su estructura en tiempo de diseño y luego valerte de los métodos Insert/Post para rellenarlo. Esto no debiera añadir mas que algunos milisegundos a la operación de tu programa, pues todo se hará en memoria.

hasta luego.

;)

sitrico 27-05-2005 17:22:40

Gracias Roman, Mick y jachguate, voy a crear un tRXMemoryData (que es en en escencia un TTable) y lo manejo todo desde memoria. Ahora el asunto es donde crear el objeto, creo que lo voy a asociar directamente a mi reporte Base (el Dataset del reporte será un tRXMemoryData) y aplicaré las inclusiones directo a él.

Otra cosa ¿ Hay alguna diferencia entre tTable.Append y tTable.Insert ?. Yo siempre he usado el insert, pero la verdad no veo diferencias reales.

// Primeros resultados

Cita:

Otra problema con esa query es que la estas limpiando y recreando continuamente (el Sql.Clear y Sql.Add) por cada registro, esto obliga al engine a parsear la sentencia cientos de veces, lo que aumenta tambien la lentitud, deberias asignar una sola vez la sentencia sql al principio, y despues simplemente solo asignar los parametros en el bucle.
Probé mover el código que genera el SQL al principio del proceso para que sólo se "parseara" una vez y la diferencia de rendimiento fue mínima (mejoró menos de 1 seg).

sitrico 27-05-2005 20:23:53

Bueno, con el RxMemoryData el rendimiento es optimo (< 1 seg) para generar los temporales y al usar directamente la tabla del reporte también me permitio eliminar algo de código :D :D

El problema (generalmente hay uno) es que los reportes me los muestra en orden inverso y no encuentro la manera de indexar ó ordenar el RxMemoryData. ¿ Alguien sabe como ?

Gracias

roman 27-05-2005 20:26:44

Cita:

Empezado por sitrico
¿ Alguien sabe como ?

Ni idea, pero si usases un ClientDataSet podrías ordenar por cualquier columna o columnas.

// Saludos

jachguate 27-05-2005 22:45:15

No tiene propiedad IndexFieldNames?

sitrico 30-05-2005 15:52:21

Bueno, y sólo para futura referencia, el RxMemoryData no tiene (hasta donde sé) capacidad para ordenar o indexar archivos (mejor dicho estructuras de datos) la solución que encontre fue sustituir el "Insert" por un "Append".

Aparentemente y respondiendo mi pregunta anterior

Cita:

Empezado por Sitrico
¿ Hay alguna diferencia entre tTable.Append y tTable.Insert ?.

El "insert" incluye el registro antes del registro donde se encuentra el apuntador de la base de datos y por esto se generaba la aparente "inversión del orden de los registros" en la base de datos. Mientras que el "append" lo agrega al final del archivo y por lo tanto no causa el problema de la inversión.

En cuanto al rendimiento :rolleyes: :) :D ahora mis reportes se generan en 1 seg.

Gracias a todos.


La franja horaria es GMT +2. Ahora son las 21:34:37.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi