본문 바로가기
프로그래밍/PC

FieldByName 사용팁

by 사악신 2012. 7. 25.

FieldByName 의 소스를 보면 다음과 같다.

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

 

3행의 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;

 

FFields.FindField 가 먼저 호출되고 그 결과에 따라 FieldList.Find 와 FAggField.Find 가 호출될 수 있다. 여기서 FFields.FindField 의 소스를 보면 다음과 같다.

function TFields.FindField(const FieldName: string): TField;
var
  I: Integer;
  HashValue: Cardinal;
begin
  if FList.Count > 0 then
  begin
    HashValue := TNamedItem.HashName(FieldName);
    for I := 0 to FList.Count - 1 do
    begin
      Result := FList.Items[I];
      if (Result.FFieldNameHashValue = HashValue) and
        (AnsiCompareText(Result.FFieldName, FieldName) = 0) then
        Exit;
    end;
  end;
  Result := nil;
end;

 

여기서 TNamedItem.HashName 클래스 메소드의 소스는 다음과 같다.

class function TNamedItem.HashName(const Name: string): Cardinal;

function GetStringLength(const S: string): Integer; inline;
var
  Ptr: IntPtr;
begin
  Ptr := IntPtr(S);
  if Ptr <> 0 then
    Result := PLongInt(Ptr - 4)^
  else
    Result := 0;
end;

const
  BufferLen = 256;
var
  Buffer: array[0..BufferLen - 1] of Char;
  Data: PChar;
  I, Len: Integer;
begin
  if Name <> '' then
  begin
    Len := GetStringLength(Name);
    if StringElementSize(Name) = 1 then
    begin
      Len := UnicodeFromLocaleChars(StringCodePage(Name), 0, Pointer(Name), Len, nil, 0);
      if Len > BufferLen then
        GetMem(Data, Len * SizeOf(Char))
      else
        Data := @Buffer[0];
      UnicodeFromLocaleChars(StringCodePage(Name), 0, Pointer(Name), GetStringLength(Name), Data, Len);
    end
    else
    begin
      if Len > BufferLen then
      GetMem(Data, Len * SizeOf(Char))
    else
      Data := @Buffer[0];
      Move(Pointer(AnsiUpperCase(Name))^, Data^, Len * SizeOf(Char));
    end;

    Result := 0;
    for I := 0 to Len - 1 do
    begin
      Result := (Result shl 5) or (Result shr 27); //ROL Result, 5
      Result := Result xor Cardinal(Data[I]);
    end;

    if Data <> @Buffer[0] then
      FreeMem(Data);
  end
  else
    Result := 0;
end;

 

장황하게 썼는데, SQLQuery.FieldByName('어쩌구').AsString 과 같은 호출 속에 이런 로직들이 사용되고 있다는 점이다. 만약, DBGrid 가 아닌 ListView 혹은 StringGrid 등에 조회한 레코드 값을 입력하는 경우라면 무심코 다음과 같이 사용할 수 있을 것이다.

 

 

 

while not SQLQuery.Eof do

begin

   ...

  SQLQuery.FieldByName('어쩌구').AsString;

  ...

end;

 

만약, 1000건의 데이터라면 1000 회의 루프 속에 상기와 같은 호출이 발생한다. 이중 루프는 물론이고... 성능 저하는 불보듯 뻔하다. 이 경우 권장 사용법은 다음과 같다.

 

var

  Field: TField

begin

  ...

  Field := SQLQuery.FieldByName('어쩌구');

  ...

  while not SQLQuery.Eof do

  begin

     ...

    Field.AsString;

    ...

  end;

 

별거 아니지만 알아도 몇 달 후면 까먹을 팁이라 이번엔 포스팅해 둔다. ^^;;

 

반응형

댓글