Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Varios (https://www.clubdelphi.com/foros/forumdisplay.php?f=11)
-   -   Problemas con DB + ListView (https://www.clubdelphi.com/foros/showthread.php?t=60546)

mightydragonlor 05-10-2008 22:10:46

Problemas con DB + ListView
 
Hola a todos, espero me puedan ayudar, mi problema es el siguiente:
Estoy intentando cargar datos desde una DB a un TListView a travez de un Query, el problema radica en que tengo muchos registros en la tabla de la DB y el proceso se ralentiza enormemente, les dejo un fragmento del código para que me ayuden a arreglar este problema.
Código Delphi [-]
procedure TMaetro.FormCreate(Sender: TObject);
var
  AItem: TListItem;
begin
If UnitStart.ClienteNQ.Consultar(CronosDB,ClienteQR,'SP_CLIENTE_SELECT') then
begin
  ListView1.Clear;
  ClienteQR.First;
  While not ClienteQR.Eof do
  begin
    AItem := ListView1.Items.Add;
    AItem.Caption := ClienteQR.Fields[0].AsString;
    AItem.SubItems.Add(ClienteQR.Fields[1].AsString);
    AItem.SubItems.Add(ClienteQR.Fields[2].AsString);
    ClienteQR.Next
  end;
end
else
MessageBox(Application.Handle,
             PChar('No es posible consultar a la Base de Datos'),
             PChar('Error de Conexion'),
             MB_OK + MB_ICONSTOP + MB_DEFBUTTON1 + MB_APPLMODAL);
end;

mightydragonlor 06-10-2008 23:04:36

Hola a todos, aquí dejo el código que he logrado optimizar, pero aún me anda muy lento, en este momento tengo 1000 registros en la tabla que estoy consultando, les agradezco cualquier ayuda.
Código Delphi [-]
procedure TMaster.ListarTabla( DB: TIBDatabase; sTabla: String; Listado: TListView );
var
  Campos: TStringList; i: Integer; Consulta: TIBQuery;
begin
  if DB = nil then Exit;
  Campos := TStringList.Create;
  DB.GetFieldNames( sTabla, Campos );
  Consulta := TIBQuery.Create( nil );
  Consulta.Transaction := CronosTR;
  Consulta.SQL.Add( 'SELECT * FROM ' + sTabla );
  try
   Consulta.Open;
  except
   CronosTR.Rollback;
   raise;
  end;
  Listado.Columns.Clear;
  Listado.Columns.Add;
  Listado.Columns[0].Width := 0;
  for i := 0 to Campos.Count - 1 do
  begin
   Listado.Columns.Add;
   Listado.Columns[i+1].Caption := Campos[i];
   Listado.Columns[i+1].Width := 100;
  end;
  Listado.Clear;
  Listado.AllocBy := Consulta.RecordCount;
  Listado.Items.BeginUpdate;
  Consulta.DisableControls;
  while not Consulta.Eof do
  begin
   Listado.Items.Add;
   for i := 0 to Campos.Count - 1 do
    Listado.Items[Listado.Items.Count-1].SubItems.Add(Consulta.FieldByName(Campos[i]).AsString);
    Consulta.Next;
  end;
  Consulta.EnableControls;
  Listado.Items.EndUpdate;
  FreeAndNil( Campos );
  FreeAndNil( Consulta );
end;

enecumene 06-10-2008 23:09:26

Hola, a ver, no creo que vaya a cargarse más rápidos con tanto registros, mi consejo sería que pusieras una especie de progressbar o un Gauge mientras se carga la lista.

Saludos.

mightydragonlor 06-10-2008 23:52:25

Gracias enecumene por responder tan rápido, ps ojalá se pueda cargar mas rápido, por que acabo de testear la conexion para saber cuanto tiempo se demora en ejecutarse y estos son los datos:
Código:

64 ms para consultar 1000 registros
19902 ms para cargar los 1000 registros en el listview

Como podemos ver, el problema se origina en el algoritmo de carga en el TListview, alguien conoce un método mejor para la carga de estos datos?

roman 07-10-2008 00:40:40

Cita:

Empezado por mightydragonlor
alguien conoce un método mejor para la carga de estos datos?

Sí, yo :)

En tu caso particular yo usaría el ListView en modo virtual. En este modo, el ListView no carga todos los datos sino sólo los que se requiere mostrar en un momento dado.

Los pasos para ello son muy sencillos:
  1. Pones la propiedad OwnerData del ListView en true.

  2. Al momento de leer los datos, en lugar de cargar los elementos del ListView como hasta ahora, simplemente le indicas cuántos elementos hay:

    Código Delphi [-]
    ListView.Items.Count := DataSet.RecordCount;
  3. Los elementos en sí, los cargas en el evento OnData del ListView

    Código Delphi [-]
    DataSet.RecNo := Item.Index; // Item es el parámetro del evento
    Item.Caption := DataSet.FieldByName(...).AsString;
    Item.SubItems(0) := DataSet.FieldByName(...).AsString;
    Item.SubItems(1) := DataSet.FieldByName(...).AsString;
    
    ...

Aquí DataSet es el data set que estés usando, query, table, etc. Eso sí, es necesario que este data set pueda recorrerse aleatoriamente (que estén implementadas las propiedades RecordCount y RecNo). De no ser así, puedes factorizar por un ClientDataSet:

Código:

TuDataSet -> DataSetProvider -> ClientDataSet -> ListView
// Saludos

mightydragonlor 07-10-2008 01:00:48

Hola roman, gracias por tu aporte, pero hay un problema, ¡¡No entendí!!:eek:, lamento no haberte entendido muy bien, en este momento lo estoy intentando, pero no logro agregar los datos del TIBQuery al TListview, agradeceria me pudieras dar mas detalles.

mightydragonlor 09-10-2008 01:30:47

Hola a todos, les cuento que por mas que he tratado, no he podido cargar mas de 1 registro en el listview, no se en donde tengo el error, agradecería si alguien me muestra cual es la solución a mi torpeza.;)

enecumene 09-10-2008 02:01:56

Creo que Roman se refería a esto:

Código Delphi [-]
procedure TMaetro.FormCreate(Sender: TObject);
var
  AItem: TListItem;
begin
If UnitStart.ClienteNQ.Consultar(CronosDB,ClienteQR,'SP_CLIENTE_SELECT') then
begin
  ListView1.Clear;
  ClienteQR.First;
  While not ClienteQR.Eof do
  begin
    ClienteQR.RecNo := AItem.Index;
    AItem.Caption := ClienteQR.Fields[0].AsString;
    AItem.SubItems(0) := ClienteQR.Fields[1].AsString;
    AItem.SubItems(1) := ClienteQR.Fields[2].AsString;
    ClienteQR.Next
  end;
end
else
MessageBox(Application.Handle,
             PChar('No es posible consultar a la Base de Datos'),
             PChar('Error de Conexion'),
             MB_OK + MB_ICONSTOP + MB_DEFBUTTON1 + MB_APPLMODAL);
end;

Saludos.

PD. Creo :D

roman 09-10-2008 06:59:21

1 Archivos Adjunto(s)
Cita:

Empezado por enecumene
PD. Creo :D

Pues no :p

Si lo haces así, queda igual que como lo estaba haciendo el compañero. El chiste es no cargar todos los datos de un sólo golpe, sino solo conforme se necesiten, esto es, conforme el usuario se va desplazando por el ListView.

Aquí les pongo un ejemplo que funciona de ambos modos: normal o virtual. En modo normal, el ListView carga los datos (+/- 4000) tal como lo hacen ustedes.

Para usar el modo virtual basta que pongan la propiedad OwnerData del ListView en true. Háganlo y verán la diferencia.

roman 09-10-2008 07:06:40

Aquí hay otro ejemplo, pero en ese caso lo que se muestra son los archivos de un directorio. Una mitad del ejemplo (SysIcons) carga en modo normal y la otra (VirtualSysIcons) en modo virtual. Pruébenlos abriendo un directorio grande (como C:\Windows\System32) y verán la diferencia.

// Saludos

enecumene 09-10-2008 16:36:51

¡Vaya!, cada día se aprende algo nuevo :D

Saludos.

mightydragonlor 10-10-2008 16:23:30

muchas gracias roman, te agradezco enormemente tu colaboración, la verdad es que me ha quedado muy claro tu ejemplo, de nuevo gracias por ayudarme.


La franja horaria es GMT +2. Ahora son las 03:07:19.

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