Ir para conteúdo
Fórum Script Brasil
  • 0

Trabalhar com dados em memória


renanbg

Pergunta

Estou com uma pequena agenda de compromissos, onde tenho uma duvida sobre trabalhar com dados em memoria, para melhorar a performance.
O funcionamento é bem simples.

2i8dmj6.png

Ao clicar num calendário, executo esse código, que varre a tabela AGENDA e conta quantos compromissos tenho por hora, listando os horários num clientdataset e exibido num dbgrid.

procedure TFrmAgenda.CarregaHorarios(f_Data: TDate);
var
  X: Integer;
  HoraIni: TTime;
begin
  // Abre e fecha o componente de memoria
  cdsHorarios.Close;
  cdsHorarios.IsEmpty;
  cdsHorarios.CreateDataSet;
  cdsHorarios.Open;
	  // Procedimento de inicio de hora
  HoraIni := StrToTime('07:15');
	  // For para ir das 08:00 as 11:45 e 13:30 as 20:15
  for X := 1 to 16 do
  begin
    HoraIni := IncMinute(HoraIni, 45);
    //Contando os registros pra colorir o grid horarios
    with qrCont do
    begin
      Close;
      SQL.Clear;
      SQL.Add('SELECT DATA, HORA FROM AGENDA');
      SQL.Add('WHERE HORA = :H AND DATA = :D');
      ParamByName('H').AsTime := HoraIni;
      ParamByName('D').AsDate := Calendario.Date;
      Open;
      FetchAll;
    end;
	    cdsHorarios.Append;
    cdsHorarios.FieldByName('HORA').AsDateTime := HoraIni;
    cdsHorarios.FieldByName('QTDE').AsInteger  := qrCont.RecordCount; 
    cdsHorarios.Post;
	    if HoraIni = StrToTime('11:45') then
       HoraIni := IncMinute(HoraIni, 60);
  end;
	  cdsHorarios.First;
  GridHorariosCellClick(GridHorarios.Columns[0]); //Mostra a agenda do 1º registro
end;

Link para o comentário
Compartilhar em outros sites

12 respostass a esta questão

Posts Recomendados

  • 0

O único trecho que me deixou um pouco em dúvida foi:

cdsHorarios.IsEmpty; // <- porque um IsEmpty sem verificação?
  cdsHorarios.CreateDataSet; // <- Não sei quantas vezes você chama esse método, mas o CreateDataSet pode ser utilizado apenas uma vez, depois disso para limpar o conjunto de dados basta utilizar cdsHorarios.EmptyDataSet;

 

Outra coisa, a query qrCont recebe o mesmo SQL 16 vezes, você pode já deixar o sql carregado nela antes do for (ou até mesmo em tempo de projeto) e no for ir alterando apenas os valores dos parâmetros, ex:

  HoraIni := StrToTime('07:15'); // For para ir das 08:00 as 11:45 e 13:30 as 20:15
  qrCont.SQL.Close;
  qrCont.SQL.Clear;
  qrCont.SQL.Add('SELECT DATA, HORA FROM AGENDA');
  qrCont.SQL.Add('WHERE HORA = :H AND DATA = :D');
  for X := 1 to 16 do
  begin
    HoraIni := IncMinute(HoraIni, 45);
    //Contando os registros pra colorir o grid horarios
    with qrCont do
    begin
      Close;
      ParamByName('H').AsTime := HoraIni;
      ParamByName('D').AsDate := Calendario.Date;
      Open;
      FetchAll;
    end;
	cdsHorarios.Append;

Do mais eu não enxerguei nada que possa estar comprometendo a performance.

Link para o comentário
Compartilhar em outros sites

  • 0

Boa tarde Leo. Obrigado por responder minhas duvidas. 

Carrego essa procedure no evento de click do TCalendar. Então a cada dia diferente que eu selecionar, estarei rodando ela.Uso o IsEmpty e o CreateDataSet por esse motivo, você acha que pode ser diferente?

Sobre o carregamento da qrCont, fiz como você orientou. A primeira vista me pareceu que o carregamento do ClientDataSet foi mais lento, mas deve ser impressão pois dessa forma ficou mais leve.

---------------------------

Se não sei importa, gostaria de uma dica pra carregar os agendamento no segundo dbgrid

Como você pode ver pela tela que postei, no primeiro dbgrid eu carrego os horários e o número de agendamentos por horário. Por meio do evento de click, abasteço o dbgrid2 com os agendamentos do horário selecinado, mas como dá pra ver, novamente estou abrindo e fechando minha tabela. Preciso achar um meio de fazer isso com os dados em memoria.

   with frmdm.qrAgenda do
   begin
     Close;
     SQL.Clear;
     SQL.Add('SELECT * FROM AGENDA');
     SQL.Add('WHERE HORA = :H AND DATA = :D');
     ParamByName('H').AsTime:= cdsHorariosHORA.AsDateTime;
     ParamByName('D').AsDate:= Calendario.Date;
     Open;
   end;

Achei uma dica aqui, porém nunca trabalhei com datasetprovider.

Uso apenas ibquery, ibupdatesql e datasource nas minhas conexões.

http://www.devmedia.com.br/trabalhando-a-propriedade-filter-do-clientdataset-parte-2/13575

Qualquer direção que puder me apontar, te agradeço.

Link para o comentário
Compartilhar em outros sites

  • 0

O que eu disse do IsEmpty é porque o mesmo é uma função que retorna se o ClientDataSet está vazio ou não, mas onde você está utilizando, você não pega o retorno dessa função, então ela está apenas gerando processamento, por menos que seja.

Quanto ao CreateDataSet, quando eu utilizo o ClientDataSet como tabela virtual, eu costumo deixar o CreateDataSet no evento OnCreate/OnShow do formulário e caso eu precise limpar esse meu conjunto de dados para fazer uma nova inserção, eu utilizo o comando EmptyDataSet.

Para não ficar consultando a tabela cada vez que o usuário clicar em um horário diferente, o que você pode fazer é carregar todos os agendamentos da data selecionada em um ClientDataSet e conforme o usuário for clicando nos horários, você filtra o CDS para exibir apenas os agendamentos desse horário, e o grid 2 estaria ligado a esse CDS.

Link para o comentário
Compartilhar em outros sites

  • 0

Bom dia Leo.

Entendi sua lógica quanto ao empty e o create.

Adicionei o create e o open no evento de create do formulario e na procedure que carrega os horarios alterei para empytdataset.

Sobre carregar todos agendamentos num cds, pelo que pesquisei(link abaixo), precisarei fazer o uso de um conjunto de componentes que seriam

 

http://www.devmedia.com.br/trabalhando-a-propriedade-filter-do-clientdataset-parte-2/13575

ibquery para fazer o select no banco

datasetprovider para fazer o meio de campo

clientdataset para receber ps dados no cliente

datasource mostrar os dados no dbgrid2

Essa seria a unica opção? Fiz as ligações como mostrado no link acima e realmente funcionou, mas como nunca trabalhei com o datasetprovider, não tenho ideia de como eu poderia fazer as inclusões e exclusões, que hoje são feitas diretamente pela query e gerenciadas pelo ibupdatesql

Editado por renanbg
Link para o comentário
Compartilhar em outros sites

  • 0

Olha, provavelmente até existem outras opções sim, mas só consegui pensar nessa.

 

Para trabalhar com inclusões e exclusões, quando é utilizado esse conjunto de componentes, ao invés de utilizar o ibquery para fazer o insert/append/delete, você vai utilizar o ClientDataSet. Acredito também que poderia continuar usando o IBUpdateSQL vinculado ao IBQuery sem problemas, mas será necessário fazer o teste.

Lembrando que após incluir/excluir algum registro com o ClientDataSet, é necessário utilizar a função ApplyUpdates do mesmo para que as alterações sejam realmente feitas no banco de dados.

Link para o comentário
Compartilhar em outros sites

  • 0

Boa tarde Leo, como vai?

Montei toda estrutura com DataSetProvider e ClientDataSet e os filtros agora estão rodando bem.

Estou enfrentando problemas de atualização no banco. Testei um delete desta forma

  if cdsAgendamentos.RecordCount > 0 then
  begin
     Mensagem := 'Deseja realmente excluir este agendamento ?';
     if Application.MessageBox(PCHAR(Mensagem), 'Fisio Soft RM', MB_ICONQUESTION + MB_YESNO + MB_DEFBUTTON2) = MRYES then
     cdsAgendamentos.Delete;
     cdsAgendamentos.ApplyUpdates(0);
  end;

O registro não é deletado no banco de dados, a menos que eu saia do sistema e entre novamente.

Se faria necessário um CommitRetaining após o ApplyUpdates?

Link para o comentário
Compartilhar em outros sites

  • 0

Estranho, fiz o teste aqui e funcionou, esse problema ocorre apenas para a exclusão ou para a inserção também?

uma coisa que eu reparei é que independente do que o usuário marcar na mensagem de confirmação de exclusão, será executado o ApplyUpdates, acredito que não seja isso, mas experimenta colocar o Delete e o ApplyUpdates entre begin e end.

O meu ficou assim:

if (Application.MessageBox(PChar('Deseja excluir o item selecionado?'), 'Teste', MB_YESNO + MB_ICONQUESTION) = mrYes) then
  begin
    ClientDataSet1.Delete;
    ClientDataSet1.ApplyUpdates(0);    
  end;

 

E logo após a confirmação, faço um select no banco e o registro foi apagado.

Link para o comentário
Compartilhar em outros sites

  • 0

Leo, testei só o delete por hora e descobri o motivo.

Após deletar, eu preciso fechar e abrir a tabela Agenda, assim o registro não aparece mais no clientdataset, mas continua no banco se você abrir a base via ibo ou ibexpert. Só sai do banco em definitivo se fecho o sistema.

Já com o Commit ele é deletado do banco na hora.

Abrir e fechar uma tabela dessas, que em um ano vai ter mais de 10.000 registros vai me causar lentidão né?

Estou tentando um select baseado na data do TCalendar, mas daí vou precisar executar esse select cada vez que o usuario clicar em outra data

 

  with frmdm.qrAgenda do
  begin
    Close;
    SQL.Clear;
    SQL.Add('SELECT * FROM AGENDA');
    SQL.Add('WHERE DATA = :DT');
    ParamByName('DT').AsDate:= Calendario.Date;
    Open;
  end;

O que você costuma fazer nesses casos?

 

Editado por renanbg
Link para o comentário
Compartilhar em outros sites

  • 0

Hum, agora está trazendo tudo.

Abro a tabela no create do sistema e dou um:

SELECT * FROM AGENDA

Isso por si só já pesaria muito e como vou ter que fechar e abrir a tabela após cada inclusão e exclusão, imagina só onde irei parar.

Acho que minha melhor saída seria um select por data, no onclick do Tcalendar, assim pelo menos traria no máximo 50 registros por busca.

SELECT * FROM AGENDA
WHERE DATA = TCALENDAR.DATE

 

Link para o comentário
Compartilhar em outros sites

  • 0

Sim, o filtro por data deve estar presente, afinal, mas porquê fechar e abrir a tabela após cada inclusão e exclusão?

aqui nos meus testes pelo menos, após o ApplyUpdates, as alterações foram transmitidas para o banco de dados normalmente, a única coisa que eu precisei fazer foi um ClientDataSet.Refresh após a inserção, pois utilizo autoincremento diretamente no banco, então sem o refresh ele não mostrava o ID.

Fiz os testes utilizando o SQL Server e os componentes TADOConnection, TADOQuery, TDataSetProvider, TClientDataSet, TDataSource e um TDBGrid, para fins de testes, não alterei nenhuma configuração dos componentes, utilizei a padrão, apenas informei o sql no TADOQuery e adicionei os campos no TADOQuery, TClientDataSet e no TDBGrid.

Nesse ClientDataSet que está ligado ao Provider, você não está utilizando o CreateDataSet não né?

Link para o comentário
Compartilhar em outros sites

  • 0

Beleza, então removi o open da abertura do projeto e estou abrindo a AGENDA no click do calendario, com parametros. Dessa forma, a cada clique no calendario executo essa rotina:

procedure TFrmAgenda.CarregaHorarios(f_Data: TDate);
var
  X: Integer;
  HoraIni: TTime;
begin
  //Carregando os agendamentos do dia
  with FrmDm.qrAgenda do
  begin
    Close;
    SQL.Clear;
    SQL.Add('SELECT * FROM AGENDA');
    SQL.Add('WHERE DATA = :DT');
    ParamByName('DT').AsDate:= Calendario.Date;
    Open;
  end;
  // Limpa os dados da memória
  cdsHorarios.EmptyDataSet;

  // Procedimento de inicio de hora
  HoraIni := StrToTime('07:15');

  //Carregando os dados da agenda
  qrCont.Close;
  qrCont.SQL.Clear;
  qrCont.SQL.Add('SELECT DATA, HORA FROM AGENDA');
  qrCont.SQL.Add('WHERE HORA = :H AND DATA = :D');

  // For para ir das 08:00 as 11:45 e 13:30 as 19:30
  for X := 1 to 15 do
  begin
    HoraIni := IncMinute(HoraIni, 45);
    //Contando os registros pra colorir o grid horarios
    with qrCont do
    begin
      Close;
      ParamByName('H').AsTime := HoraIni;
      ParamByName('D').AsDate := Calendario.Date;
      Open;
      FetchAll;
    end;

    cdsHorarios.Append;
    cdsHorarios.FieldByName('HORA').AsDateTime := HoraIni;
    cdsHorarios.FieldByName('QTDE').AsInteger  := qrCont.RecordCount; // Criei este campo para pintar o grid
    cdsHorarios.Post;

    if HoraIni = StrToTime('11:45') then
       HoraIni := IncMinute(HoraIni, 60);
  end;

  cdsHorarios.First;
  GridHorariosCellClick(GridHorarios.Columns[0]); //Mostra a agenda do 1º registro
end;

Sobre abrir e fechar a tabela, resolvi com a sua dica do Refresh. Embora o registro ainda esteja fisicamente no banco, ele não é mais mostrado no sistema. Acho que isso deve ser cache.

Meus inserts geram um codigo autoincremento via storeprocedure, então meu insert ficou assim:

O que me intriga é que não está gravando no banco após o applyupdates, fica apenas em memória e só vai gravar fisicamente quando fecho o sistema.

Para gravar direto preciso dar o commitretaining. Só não sei se isso ée correto, visto que nunca trabalhei com esses componentes.

  cdsAgendamentos.Append;
  FrmDm.spFisio.ParamByName('TABELA').AsString := 'AGENDA';
  FrmDm.spFisio.ExecProc;
  cdsAgendamentosCODIGO.AsInteger := FrmDm.spFisio.ParamByName('RESULT').AsInteger;
  cdsAgendamentosDATA.AsDateTime:= Calendario.Date;
  cdsAgendamentosHORA.AsDateTime:= cdsHorariosHORA.AsDateTime;
  cdsAgendamentos.Post;
  cdsAgendamentos.ApplyUpdates(0);
  //refaz o select para atualizar o contador
  CalendarioClick(Sender);

Sobre o creatdataset. Não, apenas dou um open no create do formulario.

Link para o comentário
Compartilhar em outros sites

Participe da discussão

Você pode postar agora e se registrar depois. Se você já tem uma conta, acesse agora para postar com sua conta.

Visitante
Responder esta pergunta...

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emoticons são permitidos.

×   Seu link foi incorporado automaticamente.   Exibir como um link em vez disso

×   Seu conteúdo anterior foi restaurado.   Limpar Editor

×   Você não pode colar imagens diretamente. Carregar ou inserir imagens do URL.



  • Estatísticas dos Fóruns

    • Tópicos
      152,3k
    • Posts
      652,4k
×
×
  • Criar Novo...