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

Codinome
Senha
Salvar informações

 Esqueci minha senha
 Novo Cadastro

  Usuários
88 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]  Entenda por que Não Devemos usar FieldByName
Publicado por rboaro : Segunda, Fevereiro 13, 2012 - 06:16 GMT-3 (1968 leituras)
Comentários 21 Comentários   Enviar esta notícia a um amigo Enviar para um amigo   Versão para Impressão Versão para impressão
Ricardo Boaro Salve Galera!!!
Essa questão de Fields[indice] ou FieldByName, é sempre polêmica, mas na minha opinião quem ler esse artigo com atenção irá abolir o uso do FieldByName em suas aplicações, assim como eu ja fiz a vários anos. Concordo que com FieldByName o código fica bem mais fácil de ser entendido. Da mesma forma sabemos que precisamos ter muito mais atenção ao usar Fields[Indice] para não passar o índice errado e fazer bobagem.

Confesso que quando parei para analisar os dois métodos era adepto do FieldByName, mas depois de analisar o Fonte do Delphi, mudei de idéia e aboli o uso do FieldByName. Sendo assim vamos analisar o fonte e entender o que o Delphi precisa fazer em um caso ou em outro. Assim terei argumentos para convencê-los a usar sempre Fields[Indice].

"Implementação do FieldByName:"
function TDataSet.FieldByName(const FieldName: string): TField;
begin
Result := FindField(FieldName);
if Result = nil then DatabaseErrorFmt(SFieldNotFound, [FieldName], Self);
end;

Bom, até agora nada de mais. Mas vamos olhar como é implementado o método FindField:

"Implementação do 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;

Tenho certeza que até aqui ainda não os convenci, vamos então analisar agora o código do FindFields do Objeto FFields que é do tipo TField

"Implementação do FindFieldsdo Objeto em FFields"
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;

Observando o código acima, vamos pensar na seguinte situação. Imaginem que temos um dataset com 20 campos e temos na posição 20 um campo calculado com o qual precisamos fazer uma soma do tipo:

"Campo calculado na posicao 20:"
valor := 0;
while not DataSet.Eof do
begin
Valor := valor + DataSet.FieldByName('campo_calculado').asCurrency;
DataSet.Next;
end;

Se tivermos neste DataSet 100.000 registros, teremos que passar pela linha de código
==
Valor := valor + DataSet.FieldByName('campo_calculado').asCurrency;
==
100.000 vezes. Um processamento razoá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 20, cada chamada de FieldByName - em nosso caso - ocasionaria um processamento de uma repetição 20 vezes até que o campo seja encontrado. Agora vamos fazer uma conta simples:

100.000 registros x 20 vezes (FieldByname) = 2000000 instruções processadas. Tenho certeza que vocês concordam comigo que é muito.

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

Vamos ver a implementação da classe TFields para ver como o mesmo processa a instrução

"Implementação em TFields:"
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;

Podemos ver que Fields é uma property indexada, com certeza é mais rápido que o método "for" do FieldByName mas vamos mais a fundo. Vamos dar uma olhadinha no método de acesso GetField:

"Metodo 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?

"Implementação do 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:

"Método Get da propriedade itens de 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 20 campos em nosso DataSet. Agora vamos pensar na conta que fizemos anteriormente.

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

Olha que diferença entre executar 2000000 de instruções e 100000. Por isto digo, dentro de laços 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_calculado').asCurrency;
DataSet.Next;
end;

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

Mas vamos deixar a coisa mais feia ainda, pensem nisso:

FieldByName('A').asInteger :=
((FieldByName('B').asInteger + FieldByName('C').asInteger)/ FieldByName('D').asInteger) * FieldByName('E')

Isto para 1000 registros, em um DataSet com 5 campos (sendo muito otimista) 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. Na minha opinião inviável. Qual a maior dificuldade de usar Fields[indice]? A legibilidade do código?
Pois bem, nada que um comentário não resolva
Field[indice] // refere-se a campo tal..

Espero ter esclarecido essa questão.
Abraço e até a próxima!



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


por: tiagoshimizu (tiagoshimizu@hotmail.com) : Fev 13, 2012 - 06:48
(Informações sobre o membro | Enviar uma mensagem) http://http://
Cara, muito boa matéria.


por: rafael.aleixo (rafael.aleixo@gmail.com) : Fev 13, 2012 - 10:59
(Informações sobre o membro | Enviar uma mensagem) http://
Interessante rboaro, uma sugestão é utilizar tipos enumerados.

abraço


por: marcosalles (salhamoda@uol.com.br) : Fev 13, 2012 - 11:40
(Informações sobre o membro | Enviar uma mensagem) http://marcosalles.wordpress.com/
Se for optar pelo modo Rad do delphi é doloroso não utilizar o fieldbyname . ja sabemos de outrora que o método é menos performático do que o Fields
Porém em modo RAD eu utilizo o fieldbyname e quando necessário utlizo ,ou uma lista ordenada ou uma lista Hash.. Prefiro a segunda abordagem , mas so a utilizao em caso extremos .


por: aoshiminamoto (aoshiminamoto@hotmail.com) : Fev 14, 2012 - 09:08
(Informações sobre o membro | Enviar uma mensagem)
Só tenho uma colocação a fazer:
Quem utiliza tal forma de calcular um campo ???


FieldByName('A').asInteger :=
((FieldByName('B').asInteger + FieldByName('C').asInteger)/ FieldByName('D').asInteger) * FieldByName('E')

De boa né pessoal, em 8 Anos como programador Delphi nunca utilizei tal forma de atribuição...
me diz em que regra de negócio esse tipo de situação se aplica ?
por que utilizar o proprio dataset para atribuição ?

Achei bem interessante o artigo, mas acho que esse último exemplo foi muito forçado.

Mesmo assim parabens pelo artigo...


por: weberdamasio (weber@produsys.com.br) : Fev 14, 2012 - 10:45
(Informações sobre o membro | Enviar uma mensagem)
Utilizar chamando direto pelo index pode funcionar em projetos que não necessitem de muita manutenção e que poucas pessoas estejam envolvidas no desenvolvimento.
Quando se trabalha em varias pessoas desenvolvendo basta que um desenvolver altere a ordem dos fields e todo o projeto para de funcionar.
Embora usar o index seja mais veloz o risco de falhas do programador é maior.


por: mukadavid (samdavid@terra.com.br) : Fev 15, 2012 - 10:27
(Informações sobre o membro | Enviar uma mensagem) http://
Acho importante o desenvolvedor saber o que ocorre por baixo do Delphi, principalmente para decidir qual a melhor técnica adotar em determinada situação. Trabalho a muitos anos em recuperação de programas legados, e abro alguns parenteses sobre a técnica adotada.
1 - Manutenção: Como já comentaram a manutenção sem utilizar fieldbyname é terrível, e a chance de ocorrer um problema por causa da alteração da posição de um campo é muito grande.
2 - Performance: Claro que a performance utilizando o índice do campo vai melhorar mas a ideia é que não se traga 100.000 registros em um dataset. Outra, o maior gargalo dos sistemas está na própria forma de fazer as consultas SQL, se vcs pararem de usar "select *" a performance do sistema irá melhorar muito.
Mas no geral, ótimo artigo, se possível poderia fazer uma analise entre acessar o Fields[] e o campo declarado no FieldEditor.


por: Caduzera (edu_carlos@ig.com.br) : Fev 16, 2012 - 08:41
(Informações sobre o membro | Enviar uma mensagem) http://
Achei muito válida a explicação do funcionamento. Se um é melhor ou pior que o outro fica a critério de cada profissional de acordo com sua aplicação.


por: ggiovanii (bruno@pirabyte.com.br) : Fev 17, 2012 - 04:43
(Informações sobre o membro | Enviar uma mensagem)
Parabéns pelo artigo! É claro que essa é uma questão complexa, acredito que depende muito do projeto, de quantas pessoas estão e estarão envolvidas durante o desenvolvimento e adequações, acho meio complicado e arriscado tratar dessa forma, mas o ganho de desempenho é considerável.


por: gnandi83 (papaicleber@hotmail.com) : Fev 28, 2012 - 09:03
(Informações sobre o membro | Enviar uma mensagem) http://http://
Não entendi o por que de não usar FieldByName!!
Também não entendi como funciona o outro processo para chamar um field sem o Byname..


por: drcoelho (davidrcoelho@gmail.com) : Fev 29, 2012 - 01:23
(Informações sobre o membro | Enviar uma mensagem)
soh utilizaria isso em casos muito especificos, os quais nunca precisei ateh hj =)..

coloco sempre em primeiro lugar a simplicidade do codigo e a facilidade de o mesmo ser entendido por outros..

outra coisa eh analise de performance sendo feita prematuramente com base apenas em analise de codigo.. trabalho com java e isso eh muito comum de ser feito.. e eh um terror.. rs

hoje temos processamento sobrando nas maquinas e as cpu cada vez mais "espertas".. entao pra saber se ha impacto em performance (CPU), apenas analisando a aplicacao em execucao..

mas vlw o artigo.. abs


por: chips90 (dennis.alves18@gmail.com) : Ago 04, 2012 - 02:20
(Informações sobre o membro | Enviar uma mensagem)
Concordo com a maioria dos colegas, quanto a utilização do fieldByName, aqui na empresa nós o utilizamos, pois a grande maioria das queries são reaproveitadas, e as vezes é efetuada uma manutenção e altera a ordem dos índices do DS, quanto a questão do desempenho, a execução do laço não torna o programa visivelmente mais lento, a lentidão aparente para um banco de 100.000 registros como o autor citou, demora o mesmo tempo no SGBD, e quanto ao último exemplo, ficou bem forçado mesmo, aqui nós sempre realizamos as somas através do SQL, creio que a maioria dos colegas desenvolvedores também fazem deste jeito...
  Edição 112

Revista ActiveDelphi

  50 Programas Fontes


  Produtos

Conheça Nossos Produtos

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