將垂直資料表 (列: 產品、月份、銷售額) 轉為交叉表 (列: 產品,欄: 月份,值: 銷售額)。使用 TClientDataSet 動態建立欄位與資料。
CreateTmpTab(FieldDefs):依欄位定義建立暫存 TClientDataSet。GenCrossTable(Source, ColField, RowField, DataField):掃描來源資料,動態新增欄位 (月份),彙總數值並輸出交叉表 DataSet。
function CreateTmpTab(const AFieldDefs: TFieldDefs): TDataSet;
var Tmp: TClientDataSet;
begin
Result := nil;
if AFieldDefs = nil then Exit;
Tmp := TClientDataSet.Create(Application);
try
Tmp.FieldDefs.Assign(AFieldDefs);
Tmp.CreateDataSet;
Result := Tmp;
except
Tmp.Free; raise;
end;
end;
function GenCrossTable(Source: TDataSet; ColField, RowField, DataField: String): TDataSet;
var
Tmp: TClientDataSet; DS: TDataSource; Strs: TStringList;
RowVal, ColVal, DataVal: String; I, J: Integer;
begin
Result := nil;
if (ColField='') or (RowField='') or (DataField='') then raise Exception.Create('欄位不可空白');
if (ColField=RowField) or (ColField=DataField) or (RowField=DataField) then raise Exception.Create('欄位不可重複');
// 僅支援字串類型欄位作為動態欄
if not (Source.FieldByName(ColField).DataType in [ftString, ftWideString, ftFixedChar, ftMemo, ftFmtMemo]) then
raise Exception.Create('動態欄位必須為字串類型');
Strs := TStringList.Create;
try
Source.First;
while not Source.Eof do
begin
ColVal := Source.FieldByName(ColField).AsString;
if (ColVal <> '') and (Strs.IndexOf(ColVal) = -1) then Strs.Add(ColVal);
Source.Next;
end;
Tmp := TClientDataSet.Create(nil);
Tmp.FieldDefs.Add(RowField, ftString, 50);
for I := 0 to Strs.Count-1 do Tmp.FieldDefs.Add(Strs[I], ftInteger);
Tmp.FieldDefs.Add('Sum', ftInteger);
Tmp.CreateDataSet;
DS := TDataSource.Create(nil); DS.DataSet := Tmp;
Source.First;
while not Source.Eof do
begin
RowVal := Source.FieldByName(RowField).AsString;
ColVal := Source.FieldByName(ColField).AsString;
DataVal := Source.FieldByName(DataField).AsString; if DataVal='' then DataVal:='0';
if Tmp.Locate(RowField, RowVal, [loPartialKey]) then
begin
Tmp.Edit;
Tmp.FieldByName(ColVal).AsInteger := Tmp.FieldByName(ColVal).AsInteger + StrToInt(DataVal);
Tmp.FieldByName('Sum').AsInteger := Tmp.FieldByName('Sum').AsInteger + StrToInt(DataVal);
Tmp.Post;
end
else
begin
Tmp.Append;
Tmp.FieldByName(RowField).AsString := RowVal;
for J := 1 to Tmp.Fields.Count-1 do Tmp.Fields[J].AsInteger := 0;
Tmp.FieldByName(ColVal).AsInteger := StrToInt(DataVal);
Tmp.FieldByName('Sum').AsInteger := StrToInt(DataVal);
Tmp.Post;
end;
Source.Next;
end;
Result := Tmp;
finally
Strs.Free;
end;
end;
var CrossDS: TDataSet;
begin
CrossDS := GenCrossTable(ADOTable1, 'Month', 'Product', 'Amount');
DBGrid1.DataSource.DataSet := CrossDS;
// CrossDS 為動態建立的 TClientDataSet,Owner 為 nil 需手動釋放
end;
ftFloat/ftCurrency。PIVOT (支援的資料庫)。