Clique para saber mais...
  Home     Download     Produtos / Cursos     Revista     Vídeo Aulas     Fórum     Contato   Clique aqui para logar | 15 de Novembro de 2025
  Login

Codinome
Senha
Salvar informações

 Esqueci minha senha
 Novo Cadastro

  Usuários
64 Usuários Online

  Revista ActiveDelphi
 Assine Já!
 Edições
 Sobre a Revista

  Conteúdo
 Apostilas
 Artigos
 Componentes
 Dicas
 News
 Programas / Exemplos
 Vídeo Aulas

  Serviços
 Active News
 Fórum
 Produtos / Cursos

  Outros
 Colunistas
 Contato
 Top 10

  Publicidade

  [Artigos]  [Intermediário] - O que é melhor usar FieldByName ou Fields?
Publicado por rboaro : Quarta, Setembro 25, 2013 - 07:55 GMT-3 (1579 leituras)
Comentários 12 Comentários   Enviar esta notícia a um amigo Enviar para um amigo   Versão para Impressão Versão para impressão
Administrador Sempre explico esse detalhe para os meus alunos, e essa semana encontrei um artigo do nosso novo colunista Rudinei Rosa, falando exatamente sobre esse assunto. Portanto segue abaixo seu artigo, leitura obrigatória para quem pensa em melhorar a performance de suas aplicações.
Isto é para ser mais um ponto de discussão entre nós, desenvolvedores Delphi.
Sempre fui fã do FieldByName(). Sempre achei que o código ficava muito mais claro com expressões do tipo FieldByName('nome_do_campo').asAlgumaCoisa do que Fields[indice].asAlguma coisa...
Há pouco tempo, em um projeto que estou trabalhando, um amigo do trabalho me pediu que evitasse a utilização de FieldByName e de imediato questionei o porquê de tal decisão. O mesmo me pediu para que eu desse uma olhada na implementação do FieldByName nos fontes da VCL do Delphi. Vou colar aqui função para vocês:
function TDataSet.FieldByName(const FieldName: string): TField;
begin
Result := FindField(FieldName);
if Result = nil then DatabaseErrorFmt(SFieldNotFound, [FieldName], Self);
end;

Bom, até agora nada. Mas vamos olhar como é implementado o método FindField:
function TDataSet.FindField(const FieldName: string): TField;
begin
Result := FFields.FindField(FieldName);
if (Result = nil) and ObjectView then
Result := FieldList.Find(FieldName);
if Result = nil then
Result := FAggFields.FindField(FieldName);
end;

Até agora ainda não temos nada de concreto sobre o motivo da não utilização do FieldByName a mim solicitada. Sendo um pouco mais persistente, vamos ver o método FindField do objeto FFields que é do tipo TField:
function TFields.FindField(const FieldName: string): TField;
var
I: Integer;
begin
for I := 0 to FList.Count - 1 do
begin
Result := FList.Items[I];
if AnsiCompareText(Result.FFieldName, FieldName) = 0 then Exit;
end;
Result := nil;
end;

Agora sim podemos concluir alguma coisa. Observando o código à cima, vamos pensar na seguinte situação. Imaginem que temos um dataset com 60 campos e temos na posição 60 um campo valorado com o qual precisamos fazer uma soma do tipo:
valor := 0;
while not DataSet.Eof do
begin
Valor := valor + DataSet.FieldByName('campo_valorado').asCurrency;
DataSet.Next;
end;

Se tivermos neste DataSet 100000 registros, teremos que passar pela linha de código

...
Valor := valor + DataSet.FieldByName('campo_valorado').asCurrency;
...

100000 vezes. Um processamento rasoável. Mas e o FieldByName? Observem que na implementação do método FindField da classe TField é utilizado um for de 0 até o número de campos para se encontrar o campo desejado e assim retornar o valor. Sendo, o nosso campo desejado, o campo de número 60, cada chamada de FieldByName - em nosso caso - ocasionaria um processamento de uma repetição 60 vezes até que o campo seja encontrado. Agora vamos fazer uma conta simples:

100000 registros x 60 vezes (FieldByname) = 6000000 instruções processadas.

Poxa, chegamos a um valor alto né.

Mas qual a solução? Fields[60]?

Vamos ver a implementação da classe TFields para ver como o mesmo processa a instrução Fields[indice]:
TFields = class(TObject)
private
FList: TList;
...
protected
...
function GetField(Index: Integer): TField;
...
public
...
property Fields[Index: Integer]: TField read GetField write SetField; default;
end;

Já podemos ver que Fields é uma property indexada. Opá, algo já nos mostra que isto pode ser mais rápido que a pesquisa com o for do método FieldByName mas vamos mais a fundo. Vamos dar uma olhadinha no método de acesso GetField:
if FSparseFields > 0 then
begin
if Index >= FSparseFields then
DatabaseError(SListIndexError, DataSet);
Result := FList[0];
Result.FOffset := Index;
end else
Result := FList[Index];

Reparem quem em nosso caso, que apenas a linha Result := FList[Index]; será acionada utilizando um TList onde são armazenados os campos de um DataSet. E como será a implementação da propriedade que define os itens de um TList?
TList = class(TObject)
private
FList: PPointerList;
...
protected
function Get(Index: Integer): Pointer;
...
public
...
property Items[Index: Integer]: Pointer read Get write Put; default;
...
end;

Por fim chegamos ao método de acesso Get da property items da classe TList:
function TList.Get(Index: Integer): Pointer;
begin
if (Index < 0) or (Index >= FCount) then
Error(@SListIndexError, Index);
Result := FList^[Index];
end;

Observem a diferença. Aqui se trabalha com Ponteiros para a localização do campo desejado. Sendo assim, o processamento desta instrução terá peso 1, mesmo que tenhamos 60 campos em nosso DataSet. Vamos voltar a conta que fizemos anteriormente:

100000 registros x 1 vez (Fields[indice]) = 100000 instruções processadas.

Olha que diferença entre executar 6000000 de instruções e 100000. Por isto digo, dentro de Loops envolvendo um campo de um DataSet com vários campos, pensem bem se vale a pena utilizar
valor := 0;
while not DataSet.Eof do
begin
Valor := valor + DataSet.FieldByName('campo_valorado').asCurrency;
DataSet.Next;
end;

ou
valor := 0;
while not DataSet.Eof do
begin
Valor := valor + DataSet.Fields[60].asCurrency; //campo_valorado
DataSet.Next;
end;

Querem algo para arrepiar os cabelos? Pensem em algo do tipo:
FieldByName('A').asInteger :=
((FieldByName('B').asInteger + FieldByName('C').asInteger)/ FieldByName('D').asInteger) * FieldByName('E')

Isto para 1000 registros, em um DataSet com 5 campos (algo bem pequeno) daria no pior caso:

1(A) x 2(B) x 3(C) x 4(D) x 5(E) x 100 = 120000 instruções processadas

Agora transportem esta situação para um DataSet com um pouco mais de campos e um pouco mais de registros. (Sai até um palavrão neste momento do pensamento de vocês, não sai?)

Observem que um comentário já torna o código mais claro. Não estou desaconselhando a utilização do FieldByName porém, temos que avaliar muito bem mesmo quando formos utilizar um simples método como este.

Autor: Rudinei Rosa
Link Original do Artigo: http://delphisempre.blogspot.com.br/2011/03/para-aqueles-que-utilizam-fieldbyname.html


Comentários Comentários
   Ordem:  
Comentários pertencem aos seus respectivos autores. Não somos responsáveis pelo seus conteúdos.


por: fllsilva (administracao@acritica.com.br) : Set 25, 2013 - 02:45
(Informações sobre o membro | Enviar uma mensagem) http://
Eu já realizaria o loop for da seguinte maneira:

i := DataSet.FieldByName('campo_valorado').Index;
for ...
Valor := valor + DataSet.Fields[i].asCurrency;
...

Deixar de usar o FieldByName é suicídio, o código se tornaria facilmente ilegível e passível de erros, basta mudar a ordem dos campos para já ocasionar problemas.


por: jose_luiz (joseluiz@digitoneletronica.com.br) : Set 25, 2013 - 06:05
(Informações sobre o membro | Enviar uma mensagem) http://
boa tarde.
assino em baixo da sua conclusão, já tive problemas com uso de FieldByName. Hoje uso somente Fields[x]..


por: StefanoTd (stefanotd@yahoo.com.br) : Set 25, 2013 - 09:04
(Informações sobre o membro | Enviar uma mensagem) http://http://
Muito bom artigo,
eu uso de uma forma um pouco diferente:

cdsProdutoDESCRICAO.asString:= 'descricao';

ou seja, o nome_datasetNOME_CAMPO.asSEU_TIPO

assim fica rápido também.


por: salubcosta (salubcosta@gmail.com) : Nov 25, 2013 - 02:51
(Informações sobre o membro | Enviar uma mensagem) http://
Agora vou pensar melhor antes de usar o camarada FieldByName... Usar o Fields não vai doer! rs

Muito bom artigo. Excelente!
Meus parabéns;


por: denisrocha (denisrocha@hotmail.com) : Jan 24, 2014 - 01:26
(Informações sobre o membro | Enviar uma mensagem) http://http://
Vou começar meu comentário com uma frase que escuto muito : "a diferença entre veneno e remédio é a dosagem".

Neste caso do FieldByName é a mesma coisa. Imagine que você tenha uma tabela com apenas 1 campo. Neste caso não vai fazer muita diferença né.
Ou se use o campo apenas uma vez. No geral, sair usando FieldByName para tudo é errado, mas abandonar de vez também não acho legal. Basta saber dosar e ver o quanto isso impacta na sua aplicação.


por: jesamarjunior (diretoria@projesa.com.br) : Mar 19, 2016 - 07:30
(Informações sobre o membro | Enviar uma mensagem) http://http://
Galera, sou novo no grupo. Gostei muito dos comentários e já fiz alguns programas com delphi e tenho de aprender muito de como ter respostas mais rápidas nos meus aplicativos. Cada instrução é importante pois o tempo de compilação de uma função depende da compilação de cada linha dentro desta função. Quando temos loops longos, aí a coisa é séria. Já passei por uma situação bem simples mas que ninguém tinha ideia da coisa: Um simples loop com um "FOR" para encontrar um valor numa sequência de 1 a 900000000, pesquisando de forma sequencial, levou mais de 1h15min. de processamento (o número pesquisado estava no final da sequência).Amigos que trabalhavam em TI chutaram 1S, 500ms, 2S etc. (Tomei várias cervejas apostando...kkk). Assim, vou mudar meu hábito e evitar usar o fielddbyname, substituindo por Fields[i], consultando o índice "i" dentro da rotina. Abs.
  Edição 112

Revista ActiveDelphi

  50 Programas Fontes


  Produtos

Conheça Nossos Produtos

Copyright© 2001-2016 – Active Delphi – Todos os direitos reservados