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;
별거 아니지만 알아도 몇 달 후면 까먹을 팁이라 이번엔 포스팅해 둔다. ^^;;
'프로그래밍 > PC' 카테고리의 다른 글
Turbo Pascal 6.0 README 파일 (0) | 2012.10.24 |
---|---|
XE2 에서 Custom VCL Style 을 리소스로 포함하여 사용하기 (1) | 2012.08.09 |
ISAPI 기반 어플리케이션 서버의 데이터베이스 커넥션 처리 (0) | 2012.07.20 |
Delphi XE2 에서 DataSnap REST Application Client (0) | 2012.07.13 |
dbExpress MySQL 5.1 한글 테이블명 사용 (0) | 2012.06.26 |
댓글