Não é a linguagem de programação que define o programador, mas sim sua lógica

FieldByName ou Fields


Vamos ver o por que o Fields[Indice] é mais rápido que o FieldByName(). Essa duvida começou por eu sempre utilizar o  FieldByName() e um colega o Fields[Indice], e quando conversamos para padronização do código, fomos atras da melhor forma da utilização. Então achamos um artigo na revista de Delphi sobre o assunto.
Vamos aos fatos então, iremos para o Delphi e ver oque cada código diz para nos 

Implementação do FindFields do 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;
Nesse código podemos observar que se uma determinada tabela conter 200 campos e temos na posição 200 um campo calculado com o qual precisamos fazer uma soma do tipo:

Campo calculado na posição 100
valor := 0;
while not DataSet.Eof do begin
   Valor := valor + DataSet.FieldByName('campo_calculado').asCurrency;
   DataSet.Next;
end;

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

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

200.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 100, cada chamada de FieldByName - em nosso caso - ocasionaria um processamento de uma repetição 100 vezes até que o campo seja encontrado. Agora vamos fazer uma conta simples:

100.000 registros x 100 vezes (FieldByname) = 10000000 instruções processadas. Tenho certeza que vocês concordam comigo que é muito. ('Imagina isso em uma grande aplicação')

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

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:

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..

Achei isso numa revista de delphi a algum tempo. Achei muito interessante e decidi compartilhar com vocês ^^  

Se gostar comente ^^ 

0 comentários:

Postar um comentário