|
Usuários |
|
64 Usuários Online
|
|
[Artigos]
[Intermediário] - O que é melhor usar FieldByName ou Fields? |
Publicado por rboaro : Quarta, Setembro 25, 2013 - 07:55 GMT-3 (1579 leituras)
12 Comentários Enviar para um amigo Versão para impressão
|
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 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.
|
[ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
por: cadumicro (cadumicro@ig.com.br) : Set 28, 2013 - 05:52 (Informações sobre o membro | Enviar uma mensagem) http://www.morfikbr.wordpress.com | Discordo do drgarcia1986 :)
Concordo com o StefanoTd,
Usando o objeto diretamente:
cdsProdutoDESCRICAO.AsString := 'Descricao';
Além de ficar mais rápido, exige que não tenhamos referencias inválidas dentro do código.
Quando implementado como fieldByName('NOME_CAMPO').Value, sendo este lento conforme artigo, ainda se o nome do campo mudar, este código ficaria incorreto, causando erro somente em tempo de execução. Se a alteração do nome do campo for necessária, este deve ser alterado no código.
| [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
por: drgarcia1986 (drgarcia1986@gmail.com) : Set 30, 2013 - 11:07 (Informações sobre o membro | Enviar uma mensagem) http://drgarcia1986.wordpress.com | Então amigo, mas como eu disse (e digo isso pois já trabalhei com esse modelo)... Trabalhando direto com o Field adicionado no DataSet, qualquer mudança na tabela/query que representa esse DataSet vai ocasionar uma exception... Obviamente por o campo não existir ou não estar mais de acordo de como foi adicionado...
Tem gente que acha isso bom, pois garante que a aplicação não vai subir se o a estrutura do banco não estiver correta, mas eu particularmente não gosto, principalmente por esse modelo obrigar o desenvolvedor a incluir muitos componente table/query criando aqueles DataModules monstruosos.
Mas de qualquer forma, é só uma opinião, depois que deixei de trabalhar com esse modelo achei muito melhor. | [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
[ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
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: ede1331 (ed_ede@msn.com) : Fev 07, 2014 - 01:18 (Informações sobre o membro | Enviar uma mensagem) http://http:// | Concordo que o FildByName não seja um bom método mas em casos em que Vc não esta carregando as Fields dentro do componente ele é uma boa solução e deixa o código bem legível.
Mas repetindo as palavras de um colega, Trabalhe com o que é mais produtivo pra ti, não importa o nome do santo, o que importa é o milagre, o cliente não quer saber qual método você utilizou ele quer ver é o programa funcionado, e quando você da muita atenção a detalhes você esquece da essência.
Mas mesmo assim não trabalho com o FieldByName. | [ Comentários não permitidos para usuários anônimos. Por gentileza, registre-se ou conecte-se ao sistema
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 |
|
|
50 Programas Fontes |
|
|
Produtos |
|
|