Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > Conexión con bases de datos
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Conexión con bases de datos

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 22-06-2004
Avatar de argami
argami argami is offline
Miembro
 
Registrado: jun 2003
Ubicación: Barcelona
Posts: 57
Poder: 21
argami Va por buen camino
Problema DBExpress muy lento

En mi empresa estamos migrando a firebird usando componentes de acceso DbExpress y TClientDataSet y DataSetProvider.

Pero antes de migrar hemos decidido hacer unas pruebas de velocidad para saber el camino que debemos coger para ello se decidio hacer pruebas sobre 3 ficheros
1 de 200.000 registros
1 de 500.000 registros
1 de 1.000.000 registros

para ello las he creado con la misma estructura y las voy rellenando con una rutina como la siguiente
Código:
CDSMenor.Open;
Gauge1.Progress := 0;
Gauge1.MaxValue:= 30000;
For I := 0 to 200000 do begin
    inc(J);
    if J = 1000 then begin
      CDSMenor.ApplyUpdates(-1);
      J:=0;
      Gauge1.Progress := Gauge1.Progress + 100;
      Update;
    end;
    CDSMenor.Append;
    CDSMenor.FieldByName('DEscripcion').AsString := 'Voy Por: ' +
IntToStr(I) + ' ' + DateTimeToStr(now) ;
    CDSMenor.Post;
  end;
El problemas es que con 170.000 el programa se queda colgado en el post es como si se tuviera que recorer toda la tabla o algu parecido eso nos preocupa ya que tenemos empresas que llenan esa cantidad (200.000) en su historico en un mes quisiera saber si alguno se ha encontrado con un problema semejante o si existe alguna explicacion logica al problema estaria muy agradecido os adjunto la estructura de la tabla a ver si con esta informacion me podeis ayudar.

Me despido agradeciendo toda la ayudade antemano
Gami

Código SQL [-]
Create Table MachaqueMenor (
  Codigo Integer not null,
  Descripcion Varchar (100),
  Campo1 Varchar(60) default 'Primer Campo',
  Campo1_2  Varchar(60) default 'Segundo Campo',
  Campo1_3  Varchar(60) default 'Tercer Campo',
  Campo1_4  Varchar(60) default 'Cuarto Campo',
  Campo1_5  Varchar(60) default 'Quinto Campo',
  Campo1_6  Varchar(60) default 'Sexto Campo',
  Campo1_7  Varchar(60) default 'Septimo Campo',
  Campo1_8  Varchar(60) default 'Octavo Campo',
  Campo1_9  Varchar(60) default 'Noveno Campo',
  Campo1_10 Varchar(60) default 'Decimo Campo',

  Campo2_1 Integer Default 21,
  Campo2_2 Integer Default 22,
  Campo2_3 Integer Default 23,
  Campo2_4 Integer Default 24,
  Campo2_5 Integer Default 25,
  Campo2_6 Integer Default 26,
  Campo2_7 Integer Default 27,
  Campo2_8 Integer Default 28,
  Campo2_9 Integer Default 29,
  Campo2_10 Integer Default 30,

  Campo3_1 Float default 3.1,
  Campo3_2 Float default 3.2,
  Campo3_3 Float default 3.3,
  Campo3_4 Float default 3.4,
  Campo3_5 Float default 3.5,
  Campo3_6 Float default 3.6,
  Campo3_7 Float default 3.7,
  Campo3_8 Float default 3.8,
  Campo3_9 Float default 3.9,
  Campo3_10 Float default 3.01,

  Campo4 Varchar(60) default 'Primer Campo',
  Campo4_2  Varchar(60) default 'Segundo Campo',
  Campo4_3  Varchar(60) default 'Tercer Campo',
  Campo4_4  Varchar(60) default 'Cuarto Campo',
  Campo4_5  Varchar(60) default 'Quinto Campo',
  Campo4_6  Varchar(60) default 'Sexto Campo',
  Campo4_7  Varchar(60) default 'Septimo Campo',
  Campo4_8  Varchar(60) default 'Octavo Campo',
  Campo4_9  Varchar(60) default 'Noveno Campo',
  Campo4_10 Varchar(60) default 'Decimo Campo',

  Campo5_1 Float default 3.1,
  Campo5_2 Float default 3.2,
  Campo5_3 Float default 3.3,
  Campo5_4 Float default 3.4,
  Campo5_5 Float default 3.5,
  Campo5_6 Float default 3.6,
  Campo5_7 Float default 3.7,
  Campo5_8 Float default 3.8,
  Campo5_9 Float default 3.9,
  Campo5_10 Float default 3.01,

  Campo6_1 Integer Default 21,
  Campo6_2 Integer Default 22,
  Campo6_3 Integer Default 23,
  Campo6_4 Integer Default 24,
  Campo6_5 Integer Default 25,
  Campo6_6 Integer Default 26,
  Campo6_7 Integer Default 27,
  Campo6_8 Integer Default 28,
  Campo6_9 Integer Default 29,
  Campo6_10 Integer Default 30,

  Campo7_1 Blob(400,1),
  Campo7_2 Blob(400,1),
  Campo7_3 Blob(400,1),
  Campo7_4 Blob(400,1),
  Campo7_5 Blob(400,1),
  Campo7_6 Blob(400,1),
  Campo7_7 Blob(400,1),
  Campo7_8 Blob(400,1),
  Campo7_9 Blob(400,1),
  Campo7_10 Blob(400,1),

  Campo8_1 Integer Default 21,
  Campo8_2 Integer Default 22,
  Campo8_3 Integer Default 23,
  Campo8_4 Integer Default 24,
  Campo8_5 Integer Default 25,
  Campo8_6 Integer Default 26,
  Campo8_7 Integer Default 27,
  Campo8_8 Integer Default 28,
  Campo8_9 Integer Default 29,
  Campo8_10 Integer Default 30,

  Campo9_1 Float default 3.1,
  Campo9_2 Float default 3.2,
  Campo9_3 Float default 3.3,
  Campo9_4 Float default 3.4,
  Campo9_5 Float default 3.5,
  Campo9_6 Float default 3.6,
  Campo9_7 Float default 3.7,
  Campo9_8 Float default 3.8,
  Campo9_9 Float default 3.9,
  Campo9_10 Float default 3.01,

  Campo10_1 Varchar(60) default 'Primer Campo',
  Campo10_2  Varchar(60) default 'Segundo Campo',
  Campo10_3  Varchar(60) default 'Tercer Campo',
  Campo10_4  Varchar(60) default 'Cuarto Campo',
  Campo10_5  Varchar(60) default 'Quinto Campo',
  Campo10_6  Varchar(60) default 'Sexto Campo',
  Campo10_7  Varchar(60) default 'Septimo Campo',
  Campo10_8  Varchar(60) default 'Octavo Campo',
  Campo10_9  Varchar(60) default 'Noveno Campo',
  Campo10_10 Varchar(60) default 'Decimo Campo',
  Primary Key(Codigo)
);

/*incremental*/
CREATE GENERATOR GEN_CodigoMenor;

/* diparador */
SET TERM !! ;
CREATE TRIGGER SET_IDTrabajo FOR MachaqueMenor
BEFORE INSERT AS
BEGIN
NEW.Codigo = GEN_ID(GEN_CodigoMenor, 1);
END !!
SET TERM ; !!
__________________
Gami

------------------------------------------------------------------
La necesidad no tolera tardanzas pero mas vale tarde que nunca
Responder Con Cita
  #2  
Antiguo 22-06-2004
Avatar de guillotmarc
guillotmarc guillotmarc is offline
Miembro
 
Registrado: may 2003
Ubicación: Huelva
Posts: 2.638
Poder: 23
guillotmarc Va por buen camino
Hola.

La verdad es que no me extraña. Tienes que pensar en la forma de trabajar del ClientDataset.

Se mantiene en memória una copia local de todos los datos del Dataset. En tu caso, con unos registros que ocuparan unos 4080 bytes (es un cálculo aproximado), estamos hablando de más de 600 Mb. de memória RAM (para 170.000 registros). Así que es natural que se pueda quedar colgado.

Tienes que cambiar de enfoque. Por ejplo. puedes vaciar el ClientDataset cada 1000 registros, cerrandolo y volviendolo a abrir vacío (para ello debe tener una cláusula where del tipo codigo = -1).

Aunque personalmente, en este tipo de importaciones masivas, prefiero utilizar directamente el SQLQuery con sentencias INSERT INTO. Nota: Igualmente puedes definir una transacción, para que solo cada 1000 registros se realize un Commit.

Saludos.
__________________
Marc Guillot (Hi ha 10 tipus de persones, els que saben binari i els que no).
Responder Con Cita
  #3  
Antiguo 22-06-2004
Avatar de argami
argami argami is offline
Miembro
 
Registrado: jun 2003
Ubicación: Barcelona
Posts: 57
Poder: 21
argami Va por buen camino
Gracias por contestar tan rapido varias cosas que me haz dicho:

Cita:
Empezado por guillotmarc
Tienes que cambiar de enfoque.
Tienes toda la razón pero con lo que me dices hay cosas que no tengo tan claras ejemplo:


Cita:
Empezado por guillotmarc
Por ejplo. puedes vaciar el ClientDataset cada 1000 registros, cerrandolo y volviendolo a abrir vacío (para ello debe tener una cláusula where del tipo codigo = -1).

He echo esta prueba y me resulta en lo mismo no me ayuda en nada.


Cita:
Empezado por guillotmarc
Aunque personalmente, en este tipo de importaciones masivas, prefiero utilizar directamente el SQLQuery con sentencias INSERT INTO.
Tienes razon y van muy rapido pero el planteamiento de programa es en 3 capas
1- Datos (DBExpress)
2- Accesso (TClientDataSet, TDataSetProvider)
3- Pantallas

Por lo cual el acceso desde las pantallas es solo a travez de los client dataset y los providers. por lo que me surge la duda de como hacer cuando no sea una modificacion masiva si no mas bien un acceso solo para pasar por ejemplo un movimiento a un histrorico de 500000 registros

Cita:
Empezado por guillotmarc
Nota: Igualmente puedes definir una transacción, para que solo cada 1000 registros se realize un Commit.
Esto me gustaria que me lo explicaras porque trabajando con dbexpress no veo como hacer lo de la transaccion (si estuviera con IBComponents no diria nada)

En todo caso te estoy agradecido por ttu ayuda y si pudieras tu o alguien mas ayudarme tambien lo agradeceria.


Saludos.
__________________
Gami

------------------------------------------------------------------
La necesidad no tolera tardanzas pero mas vale tarde que nunca
Responder Con Cita
  #4  
Antiguo 22-06-2004
Avatar de guillotmarc
guillotmarc guillotmarc is offline
Miembro
 
Registrado: may 2003
Ubicación: Huelva
Posts: 2.638
Poder: 23
guillotmarc Va por buen camino
Hola.

Cita:
Empezado por argami
Cita:
Empezado por guillotmarc
Por ejplo. puedes vaciar el ClientDataset cada 1000 registros, cerrandolo y volviendolo a abrir vacío (para ello debe tener una cláusula where del tipo codigo = -1).
He echo esta prueba y me resulta en lo mismo no me ayuda en nada.
El SQLQuery debe tener : select * from tabla where codigo = -1

Entonces cuando aplicas los cambios (cada 1000 registros), debes abrir un clientdataset vacío :

Código Delphi [-]
    if J = 1000 then begin
     CDSMenor.ApplyUpdates(-1);
     J:=0;
     CDSMenor.Close;
     CDSMenor.Open; // El ClientDataset no tendrá ningún registro

NOTA: Si miras el consumo de memória de tu aplicación, deberias observar como ahora se mantiene constante.

Cita:
Empezado por argami
Tienes razon y van muy rapido pero el planteamiento de programa es en 3 capas
1- Datos (DBExpress)
2- Accesso (TClientDataSet, TDataSetProvider)
3- Pantallas

Por lo cual el acceso desde las pantallas es solo a travez de los client dataset y los providers. por lo que me surge la duda de como hacer cuando no sea una modificacion masiva si no mas bien un acceso solo para pasar por ejemplo un movimiento a un histrorico de 500000 registros
Aunque nunca he utilizado el ClientDataset de esta forma, creo que puedes poner una sentencia INSERT INTO en el CommandText del ClientDataset, y ejecutarla mediante el Execute (y no el ApplyUpdates).

Cita:
Empezado por argami
Esto me gustaria que me lo explicaras porque trabajando con dbexpress no veo como hacer lo de la transaccion (si estuviera con IBComponents no diria nada)
Puedes manejar transacciones en los componentes dbExpress. Es decir el SQLConnection tiene los métodos BeginTrans y CommitTrans. Dado que puedes tener más de una transacción abierta para la misma conexión, deberias consultar su sintaxis en la ayuda de Delphi. Esta transacción, mientras esté abierta, afectará a todos los componentes dbExpress que se ejecuten (SQLQuerys, SQLStoredProcs, ...).

NOTA: En la capa de cliente (los ClientDatasets) no sé que se puedan manejar transacciones.

Saludos.
__________________
Marc Guillot (Hi ha 10 tipus de persones, els que saben binari i els que no).

Última edición por guillotmarc fecha: 22-06-2004 a las 16:20:23.
Responder Con Cita
  #5  
Antiguo 22-06-2004
Avatar de ruina
ruina ruina is offline
Miembro
 
Registrado: jun 2004
Posts: 196
Poder: 20
ruina Va por buen camino
En realidad en 3 capas nuca te deberias encontrar en el caso de tener que programar un movimiento de 500k registros en el cliente, ya que el cliente simplemente deberia hacer una petición a la capa intermedia de "mueveme 500k registros" y ahi donde puedes usar accesos "directos".

A la aplicación servidora se le carga bien bien de procedimientos remotos y está hecho, que quieres "abrir caja" -> procedimiento remoto, que quieres "mover a historico" -> procedimiento remoto, que quieres consultar la hora -> procedimiento remoto (bueno, en realidad nunca sabes si el ordenador cliente tiene la fecha bien puesta)

El uso del clientdataset es completamente distinto a las tradicionales Tables del BDE, un cliente solo deberia tener los records que necesita para trabajar y nunca, o casi nunca, todos los registros de la tabla subyacente.

por ejemplo, en una aplicación tradicional tu plantarias un grid con todos los artículos y esperarias a que el usuario pinche en alguno para modificar/ver o le dé a las opciones que hacen el locate.
En una aplicación 3 capas deberias preguntar antes al usuario que artículos quiere, y filtrar los que coincidan. Si elije "todos" ya sabe a lo que se expone, a traerse equismil registros por la red a su ordenador.
Si elije "articulos que empiecen por BAR", se le sirve el codigo, descripción y stock (por ejemplo) de esos artículos exclusivamente.

En 3 capas no es que no se pueda trabajar con transacciones en los clientes (se me ocurren un par de métodos trasgresores), es que no se DEBE, ya que las actualizaciones ya son lo suficientemente atómicas (osea, pequeñas).
Olvidate del tipo que le da a "nuevo pedido" y se va a tomar un café dejandote la transación abierta... ahora el buen hombre dejará su pedido a medias y cuando vuelva y lo complete se lanzará al servidor todos los cambios, si su pedido ya no es válido porque el café ha sido demasiado largo y no queda stock se resolverá en la capa servidora, obteniendo un bonito mensaje de error (o un formulario de resolución, que, realmente, es la pesadilla de cualquier usuario que no se lee el manual)


Realmente programando con esta mentalidad se consigue mas velocidad, ya que deberia funcionar incluso con poco ancho de banda (tengo incluso una appicación 3 capas corriendo sobre RDSI).


Eso si, el enfoque es completamente distinto y cuesta verlo (al menos a mi me costo lo suyo)
Recomiendo "la cara oculta de delphi" (ian marteens) como libro de cabecera... insisto en que no me llevo comisión (creo que es el tercer post en el que pongo esto, a este paso me cambian el nick a Amazon)
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 21:44:59.


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
Copyright 1996-2007 Club Delphi