[DELPHI] FIELDBYNAME OU FIELDS, QUAL USAR?

Sempre usei FieldByName() por achar que o código ficava mais claro com expressões do tipo FieldByName('nome_do_campo').asTipoDoCampo do que Fields[indice].asTipoDoCampo...

Depois de ler alguns tutoriais na internet penso duas vezes antes de usa-lo, após analisar sua implementação podemos concluir que dependendo da quantidade de Fields e registros o processamento pode ser muito custoso.

Implementação do FieldByName nos fontes da VCL do Delphi.

function TDataSet.FieldByName(const FieldName: string): TField;
begin
  Result := FindField(FieldName);
  if Result = nil then
    DatabaseErrorFmt(SFieldNotFound, [FieldName], self);
end;

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

Agora 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;

Observando os códigos à cima, vamos imaginar a seguinte situação. Temos um dataset com 30 campos e temos na posição 30 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 100.000 registros, teremos que passar pela linha de código entre "begin...end" 100.000 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 30, cada chamada de FieldByName (em nosso exemplo) ocasionaria um processamento de repetição de 30 vezes até que o campo seja encontrado. Agora vamos fazer uma conta simples: 

100.000 registros x 30 vezes (FieldByname) = 3.000.000 de instruções processadas. 

Se usarmos a SOLUÇÃO Fields[30]?

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. Algo já nos mostra que isto pode ser mais rápido que a pesquisa com o for do método FieldByName. Vamos olhar 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 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 que 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 30 campos em nosso DataSet. Vamos refazer a conta:

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

Podemos notar uma GRANDE diferença entre executar 3.000.000 de instruções e 100.000. Então, 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;

Não estou dizendo para NUNCA utilizar o FieldByName porém, temos que avaliar cada situação em vez de sair utilizando em toda a aplicação.


//**FieldByName melhorado**
var
  I: TField;
begin
  I := dataset.FieldByName('campo_valorado');
  while not dataset.Eof do
  begin
    valor := valor + I.asCurrency;
    dataset.Next;

  end;
end;

Gostou? Deixe seu comentário... Convido você a seguir meu blog, sua presença é bem vinda!【ツ】

Um comentário: