MyBaseを試してみる。(グループ化)

TClientDataSetのグループ化については、Helpに解説がありますが、これが私にとって大変難解な文章です。図を見ていると、できることはわかるのですけど、では実際どうしたらいいのかがさっぱり理解できません。

Embarcadero Product Documentation Wikis
インデックスを使ってデータをグループ化する

とりあえず試してみました。まずグループ化を使わずに普通にプログラムするとこんな感じです。
// サンプルデータを追加します。
procedure MakeSample(CDS: TClientDataSet);
var
  F: Boolean;
begin
  F := CDS.Active;
  if not F then
    CDS.Open;
  try
    // サンプルデータの追加
    with CDS do
    begin
      Appendrecord(['Delphi', 'ESD','Starter',18000]);
      Appendrecord(['Delphi', 'ESD','Professional',94000]);
      Appendrecord(['Delphi', 'ESD','Enterprise',236000]);
      Appendrecord(['Delphi', 'ESD','Ultimate',356000]);
      Appendrecord(['Delphi', 'ESD','Architect',416000]);
      Appendrecord(['RAD Studio', 'ESD','Professional',148000]);
      Appendrecord(['RAD Studio', 'ESD','Enterprise',336000]);
      Appendrecord(['RAD Studio', 'ESD','Ultimate',456000]);
      Appendrecord(['RAD Studio', 'ESD','Architect',516000]);
      Appendrecord(['Delphi', 'Package','Professional',98000]);
      Appendrecord(['Delphi', 'Package','Enterprise',240000]);
      Appendrecord(['Delphi', 'Package','Architect',420000]);
      Appendrecord(['RAD Studio', 'Package','Professional',152000]);
      Appendrecord(['RAD Studio', 'Package','Enterprise',340000]);
      Appendrecord(['RAD Studio', 'Package','Architect',520000]);
      CheckBrowseMode;
    end;
  finally
    if not F then
      CDS.Close;
  end;
end;

// データベースの作成
procedure CreateDB(CDS: TClientDataSet);
var
  I: Integer;
begin
  // データベースの作成
  CDS.Close;
  CDS.FieldDefs.Add('ITEM',ftWideString,20);
  CDS.FieldDefs.Add('KIND',ftWideString,20);
  CDS.FieldDefs.Add('DETAIL',ftWideString,20);
  CDS.FieldDefs.Add('PRICE',ftCurrency);

  with CDS.IndexDefs.AddIndexDef do
  begin
    Name := 'IDX';
    Fields := 'ITEM;KIND';
  end;
  CDS.CreateDataSet;
  CDS.Close;

  for I := 0 to CDS.FieldDefs.Count - 1 do
    CDS.FieldDefs[I].CreateField(CDS);

  // 集合フィールド
  with TAggregateField.Create(CDS) do
  begin
    AlignMent := taRightJustify;
    FieldKind := fkAggregate;
    FieldName := 'COUNT';
    IndexName := 'IDX';
    Expression := 'COUNT(PRICE)';
    Active := True;
    DataSet := CDS;
  end;

  with TAggregateField.Create(CDS) do
  begin
    AlignMent := taRightJustify;
    FieldKind := fkAggregate;
    FieldName := 'TOTAL_PRICE';
    IndexName := 'IDX';
    Expression := 'SUM(PRICE)';
    Active := True;
    DataSet := CDS;
  end;
  CDS.AggregatesActive := True;

  // インデックスの設定
  CDS.IndexName := 'IDX';

  // 表示用にフィールド幅を設定
  CDS.FieldByName('ITEM').DisplayWidth := 10;
  CDS.FieldByName('KIND').DisplayWidth := 10;
  CDS.FieldByName('DETAIL').DisplayWidth := 15;
  CDS.FieldByName('PRICE').DisplayWidth := 8;

  // CDS.AggregatesActive := True;
  // DBEditの設定
  Form1.DBEdit1.DataField := 'COUNT';
  Form1.DBEdit2.DataField := 'TOTAL_PRICE';
  CDS.Open;

  // サンプルデータの作成
  MakeSample(CDS);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  CreateDB(ClientDataSet1);
end;

同じデータがある場合、普通に表示するとDBGridでは、次のように表示されます。

Nogrouping



グループ化すると同じデータは、一度だけ描画されるだけになり、見やすくなるということで、次のように修正しました。
// データベースの作成
procedure CreateDB(CDS: TClientDataSet);
var
  I: Integer;
begin
  // データベースの作成
  CDS.Close;
  CDS.FieldDefs.Add('ITEM',ftWideString,20);
  CDS.FieldDefs.Add('KIND',ftWideString,20);
  CDS.FieldDefs.Add('DETAIL',ftWideString,20);
  CDS.FieldDefs.Add('PRICE',ftCurrency);

  with CDS.IndexDefs.AddIndexDef do
  begin
    Name := 'IDX';
    Fields := 'ITEM;KIND';
    GroupingLevel := 1;
  end;
  CDS.CreateDataSet;
  CDS.Close;

  for I := 0 to CDS.FieldDefs.Count - 1 do
    CDS.FieldDefs[I].CreateField(CDS);

  // 集合フィールド
  with TAggregateField.Create(CDS) do
  begin
    AlignMent := taRightJustify;
    FieldKind := fkAggregate;
    FieldName := 'COUNT';
    GroupingLevel := 1;
    IndexName := 'IDX';
    Expression := 'COUNT(PRICE)';
    Active := True;
    DataSet := CDS;
  end;

  with TAggregateField.Create(CDS) do
  begin
    AlignMent := taRightJustify;
    FieldKind := fkAggregate;
    FieldName := 'TOTAL_PRICE';
    GroupingLevel := 1;
    IndexName := 'IDX';
    Expression := 'SUM(PRICE)';
    Active := True;
    DataSet := CDS;
  end;
  CDS.AggregatesActive := True;

  // インデックスの設定
  CDS.IndexName := 'IDX';

  // 表示用にフィールド幅を設定
  CDS.FieldByName('ITEM').DisplayWidth := 10;
  CDS.FieldByName('KIND').DisplayWidth := 10;
  CDS.FieldByName('DETAIL').DisplayWidth := 15;
  CDS.FieldByName('PRICE').DisplayWidth := 8;

  // グループ化されたフィールドの表示設定
  CDS.FieldByName('ITEM').OnGetText := Form1.ITEMGetText;

  // DBEditの設定
  Form1.DBEdit1.DataField := 'COUNT';
  Form1.DBEdit2.DataField := 'TOTAL_PRICE';
  CDS.Open;

  // サンプルデータの作成
  MakeSample(CDS);
end;

// ITEM表示用
procedure TForm1.ITEMGetText(Sender: TField; var Text: string;
  DisplayText: Boolean);
begin
  // GetGroupStateについて Helpより引用
  //
  // "AggregatesActive が false の場合,または現在の
  //  インデックスがグループ化をサポートしていない場合,
  //  GetGroupState は空のセットを返します。"

  // 同じITEMの場合、一番最初のみ表示
  if (gbFirst in ClientDataSet1.GetGroupState(1)) then
    Text := Sender.AsString
  else
    Text := '';
end;

各レコード毎に表示されていたITEMがすっきりとしています。 又、Delphiを選んだ場合、Rad Studioを選んだ場合と異なったアイテム数、合計金額が表示されます。このことから、同じITEMに対して処理がされていることがわかります。

Grouping1_1


Grouping1_2



KINDフィールドも同じものが並んでいるので、こちらもグループ化させるために次のようにプログラムを修正しました。
// データベースの作成
procedure CreateDB(CDS: TClientDataSet);
var
  I: Integer;
begin
  // データベースの作成
  CDS.Close;
  CDS.FieldDefs.Add('ITEM',ftWideString,20);
  CDS.FieldDefs.Add('KIND',ftWideString,20);
  CDS.FieldDefs.Add('DETAIL',ftWideString,20);
  CDS.FieldDefs.Add('PRICE',ftCurrency);

  with CDS.IndexDefs.AddIndexDef do
  begin
    Name := 'IDX';
    Fields := 'ITEM;KIND';
    GroupingLevel := 2;
  end;
  CDS.CreateDataSet;
  CDS.Close;

  for I := 0 to CDS.FieldDefs.Count - 1 do
    CDS.FieldDefs[I].CreateField(CDS);

  // 集合フィールド
  with TAggregateField.Create(CDS) do
  begin
    AlignMent := taRightJustify;
    FieldKind := fkAggregate;
    FieldName := 'COUNT';
    GroupingLevel := 2;
    IndexName := 'IDX';
    Expression := 'COUNT(PRICE)';
    Active := True;
    DataSet := CDS;
  end;

  with TAggregateField.Create(CDS) do
  begin
    AlignMent := taRightJustify;
    FieldKind := fkAggregate;
    FieldName := 'TOTAL_PRICE';
    GroupingLevel := 2;
    IndexName := 'IDX';
    Expression := 'SUM(PRICE)';
    Active := True;
    DataSet := CDS;
  end;
  CDS.AggregatesActive := True;

  // インデックスの設定
  CDS.IndexName := 'IDX';

  // 表示用にフィールド幅を設定
  CDS.FieldByName('ITEM').DisplayWidth := 10;
  CDS.FieldByName('KIND').DisplayWidth := 10;
  CDS.FieldByName('DETAIL').DisplayWidth := 15;
  CDS.FieldByName('PRICE').DisplayWidth := 8;

  // グループ化されたフィールドの表示設定
  CDS.FieldByName('ITEM').OnGetText := Form1.ITEMGetText;
  CDS.FieldByName('KIND').OnGetText := Form1.KINDGetText;

  // CDS.AggregatesActive := True;
  // DBEditの設定
  Form1.DBEdit1.DataField := 'COUNT';
  Form1.DBEdit2.DataField := 'TOTAL_PRICE';
  CDS.Open;

  // サンプルデータの作成
  MakeSample(CDS);
end;

procedure TForm1.KINDGetText(Sender: TField; var Text: string;
  DisplayText: Boolean);
begin
  // 同じKINDの場合、一番最初のみ表示
  if (gbFirst in ClientDataSet1.GetGroupState(2)) then
    Text := Sender.AsString
  else
    Text := '';
end;

これでKINDもグループ化された表示になりました。集合フィールドで設定したGroupingLevelによって、 ITEM数、合計金額についてもKINDに対して処理がされています。ここでようやくヘルプの「グループ化レベルは,インデックス内の項目順序に対応しています。 」の意味が理解できました(^-^)

Grouping2_1

Grouping2_2

|