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;
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');
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!【ツ】
Gostou? Deixe seu comentário... Convido você a seguir meu blog, sua presença é bem vinda!【ツ】
Muito bom ! parabéns pelo post
ResponderExcluir