首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi 動態建立交叉表 (CrossTab) 以 ClientDataSet 實作

     編輯:Delphi

需求

將垂直資料表 (列: 產品、月份、銷售額) 轉為交叉表 (列: 產品,欄: 月份,值: 銷售額)。使用 TClientDataSet 動態建立欄位與資料。

核心函數

  1. CreateTmpTab(FieldDefs):依欄位定義建立暫存 TClientDataSet。
  2. 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。
  • 動態欄位需為字串類型;若為日期可先轉字串 (FormatDateTime)。
  • 大量資料建議先建立索引或使用 SQL PIVOT (支援的資料庫)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved