Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   MS SQL Server (https://www.clubdelphi.com/foros/forumdisplay.php?f=23)
-   -   Insert masivo de datos, cada vez mas lento (https://www.clubdelphi.com/foros/showthread.php?t=78745)

papote654 14-05-2012 18:25:17

Insert masivo de datos, cada vez mas lento
 
Gracias anticipadas por su ayuda.

Estoy haciendo un insert masivo de datos en SQL 2008 los cuales lo obtengo de un archivo .txt, el caso es que recorro una carpeta con 80 archivos .txt podrían ser más o menos, lleno una variable con los datos del archivo y luego lo inserto en SQL y así sucesivamente, el problema está en que mientras más datos mando a insertar más lento se va haciendo el proceso del insert.

Si inserto un .txt de 1kb solo dura unas cuantas milésimas de segundo, continuando inserto un .txt de 500kb y dura 30 segundos, continuando inserto un .txt de 1kb pero este en vez de durarme unos cuantos milésimas de segundo como el primero me dura varios segundo, es decir al momento de insertar en SQL cada vez se pone más lento.

Espero haberme dado a entender, agradeceré cualquier colaboración.

Gracias...

mightydragonlor 14-05-2012 18:37:06

Como lo haces? cual es el código con el que haces esto? desde donde lo haces? delphi, .Net, desde el mismo SQL, danos mas datos, así te podremos ayudar.

papote654 14-05-2012 19:29:32

Gracias mightydragonlor por responder, lo hago con delphi 7, en una carpeta tengo un simnumero de archivos.txt que son las tablas a exportar, leo esta carpeta y recorro cada uno de esos archivos, "todo esto es muy rapido", dentro de estos archivos estan cada uno de los registros los cuales los cargo en una variable para luego insertarlo en la db.

--esta es la idea de como quedan
Código SQL [-]
INSERT INTO tabla (campos) VALUES ("datos")
INSERT INTO tabla (campos) VALUES ("datos")
INSERT INTO tabla (campos) VALUES ("datos")

Imagina que tengo 10,000 registros en un archivo, pues el insert estara 10,000 veces y lo ejecuto todo junto en un solo comando como se ve en el codigo anterior.

Gracias..

mightydragonlor 14-05-2012 19:56:19

Entiendo, y creo armas una variable con 10 mil líneas de insert into y luego la mandas a la base de datos, lo que sucede con esto es que se genera una transacción gigantesca que hacer que la siguiente línea aún guarda información de la transacción anterior, te recomiendo que entre cada línea pongas un GO, ejemplo:

Código SQL [-]
INSERT INTO tabla (campos) VALUES ("datos")
GO
INSERT INTO tabla (campos) VALUES ("datos")
GO
INSERT INTO tabla (campos) VALUES ("datos")
GO

También que desactives la transacción desde delphi, con esto debería mejorar notablemente.

Saludos.

papote654 14-05-2012 19:59:17

Cuando dices desactive las transacciones de delphi a que te refieres?, tambien pense en crear un BEGIN TRANS en el codigo de SQL.

mightydragonlor 14-05-2012 20:01:54

dependiendo del control que uses para la conexión de base de datos, tienes un componente Transaction, con ropiedades, autocommit, active y demás, si este es el caso, lo pones en active false y listo.

Saludos.

papote654 14-05-2012 20:04:52

Entiendo, utilizo ADOQuery. Este ejecura un commit cuando hace el ExecSQL, pero no tengo propiedades para desactivarlo.

mightydragonlor 14-05-2012 20:18:08

Cita:

Empezado por papote654 (Mensaje 432565)
Entiendo, utilizo ADOQuery. Este ejecura un commit cuando hace el ExecSQL, pero no tengo propiedades para desactivarlo.

Para este caso solo haz lo de los GO, olvídate de la transacción, nos cuentas como te va en la prueba.

Saludos.

maeyanes 14-05-2012 22:08:36

Hola...

Yo te recomiendo que los commits lo hagas cada n inserciones, no después de cada una. Yo cuando he tenido que importar grandes cantidades de datos a una base de datos hago los commits cada 500 registros.


Saludos...

Casimiro Notevi 14-05-2012 22:29:46

Cita:

Empezado por maeyanes (Mensaje 432576)
Hola...
Yo te recomiendo que los commits lo hagas cada n inserciones, no después de cada una. Yo cuando he tenido que importar grandes cantidades de datos a una base de datos hago los commits cada 500 registros.
Saludos...

Por supuesto, hacerlo cada registro es una pérdida tremenda de tiempo.

papote654 15-05-2012 03:38:46

He realizado las pruebas con las recomendaciones que me han hecho pero la verdad, para mi sorpresa insertando todo de una vez es mas rapido que hacerlo por lotes. El codigo es este.

el archivo esta compuesto por datos como este.

esa primera parte hasta el simbolo > es la condicion para poder validar si el registro existe, hago esto al momento de exportar ya que se supone que esos valores talvez no existan en la tabla destino.

WHERE Se_codigo_pais = '187' AND Se_codigo_ciudad = '3323' AND Se_codigo = '5'>'5','3323','187','VILLA DUARTE'

Código Delphi [-]
Procedure Tfrm_configuracion.Importar;
Var
   F: TextFile;
   sLinea: String;
   sArchivo, DatosInsert, Tabla, varValidaExiste, Condicion: String;
   SR: TSearchRec;
Begin
   //ruta de los archivos
   sArchivo := varRutaArchivo;

   DatosInsert := '';
   varValidaExiste := '';

   //busca todos los archivos de la carpeta definida
   If FindFirst(sArchivo + '\*.txt', faArchive, SR) = 0 Then
      Begin
         //recorre los archivos de la carpeta
         Repeat

            //asigna el archivo
            AssignFile(F, sArchivo + '\' + SR.Name);

            //abro el archivo
            Reset(F);

            //captura el nombre real de la tabla
            Tabla := FtNombreTabla(copy(SR.Name, 18, 100));

            //busca los campos del servidor de destino
            Buscar_Campos(Tabla, servidor_destino);

            While Not Eof(F) Do
               Begin
                  //recorre los datos del archivo
                  ReadLn(F, sLinea);

                  Condicion := ftCondicion(sLinea);

                  //construyo el insert
                  varValidaExiste := 'IF EXISTS(SELECT 1 FROM ' + Tabla + ' ' + Condicion + ') DELETE FROM ' + Tabla + ' ' + Condicion;

                  DatosInsert := DatosInsert + varValidaExiste + ' INSERT INTO ' + Tabla + '(' + CamposOrdenados + ')' + ' VALUES(' + Copy(sLinea, PosicionesCondicion, Length(sLinea)) + ')';
               End;

            //inserta datos en la tabla de destino.
            Inserta_datos(DatosInsert);

            //cierra el archivo
            CloseFile(F);

            //muevo el archivo a otra carpeta de backup
            MoveFile(PChar(varRutaArchivo + '\' + SR.Name), PChar(varRutaBackup + '\' + SR.Name));

         Until FindNext(SR) <> 0; //cuando termine de recorrer los archivos de la carpeta

         //cierra el fichero de la carpeta donde estan los archivos
         FindClose(SR);
      End;
End;

Procedure Tfrm_configuracion.Inserta_datos(DatosInsert: String);
Var
   Qry: TADOQuery;
Begin
   Qry := TADOQuery.Create(Self);

   Qry.Connection := servidor_destino;

   With Qry, SQL Do
      Begin
         Close;
         Clear;
         Add(DatosInsert);
         ExecSQL;
      End;

   Qry.free;
End;

Casimiro Notevi 15-05-2012 09:27:39

Creo que ganarás más tiempo si usas parámetros en lugar de componer la sentencia, más o menos, algo parecido a este ejemplo:

Código Delphi [-]
dataset.close;
dataset.sql.text:='insert into tabla values :campo1, :campo2, campo3';
dataset.prepare;

while not eof(loquesea) do
begin
  dataset.paramsbyname('campo1').value := loquesea;
  dataset.paramsbyname('campo2').value := otroloquesea;
  dataset.paramsbyname('campo3').value := algoloquesea;
  dataset.execquery;
end;
dataset.transaction.commit;

sinalocarlos 16-05-2012 01:22:19

Ademas de las recomendaciones que te han hecho, una forma de identificar problemas es revisando del lado del servidor de SQL que esta pasando, para ello esta el profiler, crea una traza con el template Standar y vigila que se esta mandando desde tu programa.

Te lo comento porque a mi me paso algo por el estilo, después de volver a escribir media librería me di cuenta de que no estaba limpiando la propiedad SQL, por que lo que en realidad estaba enviando un acumulado de inserts cada vez :)

Lepe 16-05-2012 09:50:27

Y por supuesto no crees una consulta por cada inserción.

Crea la consulta en Importar y pásala por parámetro a Inserta_datos. Después la destruyes al finalizar Importar.

Si a eso le añades los parámetros que dice Casimiro, mucho mejor.

También voto hacer el commit cada 500 registros.

Saludos.

gatosoft 16-05-2012 18:02:17

Yo tengo dos recomendaciones...

primera cambiar la forma de lectura dle archivo ... (Assignfile, Reset, etc) cargandolo en un StringList mediante StringList1.LoadFromfile()... creo que es mas rápido


Lo segundo es mirar otra alternativa de carga... si trabajas con MS SQL puedes valerte de la funcionalidad "BULK INSERT" ... puedes mirar la documentación de M$ o puedes buscar en google en los foros y páginas de otras personas....

Tiene una "limitante" y es que la ruta que defines para el cargue del archivo debe estar en el servidor del motor...

Código SQL [-]
BULK INSERT TmpTabla FROM 'c:\ArchivoEnElServidorDeBD.txt' WITH (FIELDTERMINATOR = '","')

Un saludo,


La franja horaria es GMT +2. Ahora son las 06:11:07.

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