|
Usuários |
|
88 Usuários Online
|
|
[Artigos]
Entenda por que Não Devemos usar FieldByName |
Publicado por rboaro : Segunda, Fevereiro 13, 2012 - 06:16 GMT-3 (1968 leituras)
21 Comentários Enviar para um amigo Versão para impressão
|
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 pertencem aos seus respectivos autores. Não somos responsáveis pelo seus conteúdos. |
por: morfiuz (adrylb@gmail.com) : Fev 13, 2012 - 11:00 (Informações sobre o membro | Enviar uma mensagem) http://l2alone.com.br | é por isso que opto por usar TUpdateSQL e criar os TFields na TQuery, alem do código ficar legível, não corro risco de errar ao chamar um FieldByName, muito menos deixar o meu código sujo usando Fields[index]...
Se acoplar os DBEdits no formulário usando TUpdateSQL, minhas consultas dependem ainda menos de código!
Se existe o objeto para ser instanciado pra que "reinventar a roda" rescrevendo tudo de novo?
Mas gostei da comparação apesar de que o processamento hj em dia é bem mais livre... Aonde um simples select, ou update de 1000 registros não leva mais de 0,03ms!
Abraços | [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
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.
|
[ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
por: leonammaia (leonam@beginsoft.com.br) : Fev 15, 2012 - 10:18 (Informações sobre o membro | Enviar uma mensagem) | Ao ler o artigo percebi um exagero enorme do autor.
A performance não está no uso do FieldByName e sim em usá-lo dentro de um "laço". O ideal é criar uma variável externa ao "laço" com o valor do campo e utilizar essa variável dentro do "laço".
Outro aspecto é usar o Fields[i] e colocar um comentário ao lado. O código fica muito poluído com tantos comentários.
Na minha opnião o uso do FieldByName é ainda a melhor opção. | [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
por: leonammaia (leonam@beginsoft.com.br) : Fev 15, 2012 - 10:21 (Informações sobre o membro | Enviar uma mensagem) | Ao ler o artigo percebi um exagero enorme do autor.
A performance não está no uso do FieldByName e sim em usá-lo dentro de um "laço". O ideal é criar uma variável externa ao "laço" com o valor do campo e utilizar essa variável dentro do "laço".
Outro aspecto é usar o Fields[i] e colocar um comentário ao lado. O código fica muito poluído com tantos comentários.
Imagine alterando a posição dos campos dentro do ClientDataSet? mudou o código todo e o trabalho de alterar os índices do Fields[i].
Atualmente 99% dos usuários possuem boa máquinas, então o consumo de processamento do FieldByName se torna imperceptível e muito mais vantajoso falando em manutenção do software.
Na minha opnião o uso do FieldByName é ainda a melhor opção. | [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
por: Toniran (toniran@ig.com.br) : Fev 15, 2012 - 10:34 (Informações sobre o membro | Enviar uma mensagem) http://http:// | Resolução do Problema do FieldByName em Loop. Apenas uma questão de utilizar os recursos da linguagem e ponteiros/referência.
begin
var
Valor: Double;
Field: TField;
begin
Valor := 0;
Field := DataSet.FieldByName('NomeDoCampo');
//Executa o FieldByName apenas uma vez.
while not DataSet.Eof do
begin
Valor := Valor + Field.AsCurrency; //campo_calculado
DataSet.Next;
end;
Simples, prático e funcional. | [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
por: breite (breite@msn.com) : Fev 19, 2012 - 11:16 (Informações sobre o membro | Enviar uma mensagem) http:// | Concordo plenamente!
Em situações onde existe muitos registros para serem computados, é uma boa saída o uso dos índices.
Entretanto dizer:
"Entenda por que Não Devemos usar FieldByName ..."
é uma forma equivocada de expressar sua opinião.
Exitem casos e casos, sou a favor de usar as duas formas!!!
| [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
por: chips90 (dennis.alves18@gmail.com) : Ago 04, 2012 - 02:16 (Informações sobre o membro | Enviar uma mensagem) | Concordo com o colega breite, é como:
..."Não usarás GOTO"...
É claro que há situações e situações, depende da finalidade, da frequencia da manutenção, se a query será reaproveitada entre outros fatores não adianta querer citar uma "regra", porque no final das contas, o programador tem que se adequar com a finalidade do código. | [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
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: 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: 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...
|
por: Kirk_guitar (artur_alencar@hotmail.com) : Fev 28, 2014 - 05:35 (Informações sobre o membro | Enviar uma mensagem) http:// | Concordo! No ERP que temos aqui na empresa eu faço cálculos e loops diretamente na query usando FieldByName e não acho o processamento lento. Acredito que o processamento se tornaria lento usando FieldByName apenas se fossemos pegar todos os dados de uma tabela e passar para outra tabela usando duas querys. Mas como na maioria dos casos não é isso que acontece, então...
Por exemplo: eu tenho uma tabela de itens de pedido com uma média de uns 40 campos. Dou insert nesta tabela usando FieldByName('campo').AsAlgumaCoisa := outraquery.FieldByName('campo').AsAlgumacoisa. Mas faço isso só para alguns campos da tabela de itens (uns 5 ou 6). Porque o resto são resultados de cálculos feitos com outras variáveis. Então na maior parte dos códigos eu tenho isso:
FieldByName('campo').AsAlgumaCoisa := valorDaVariavel.
Levando em conta que tenho 40 campos( e que nem sempre aquele campo em questão vai ser o último da tabela), e que estou inserindo 50 itens, vou ter no final 40 x 50 = 2000 execuções de processo. Isso para computadores de hoje em dia não representa muita coisa...rsrsrs | [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
|
|
Edição 112 |
|
|
50 Programas Fontes |
|
|
Produtos |
|
|