0

到目前为止,我已经花了几天的时间为在我的应用程序中使用 FastReport 奠定基础。该应用程序以 DBF 文件的形式存储设备测试结果数据,该文件包括几个固定字段(DeviceID、Passed 等)以及可变数量的结果字段,每个字段对应于可用的测量数据类型。这些字段少至一个,多至 100 个。每个字段都有一个字母代码名称,例如 OV 和 RV。总记录数可以从零到几十万。

一个特定的报告模板已经在其设计中包含了它将显示的字段名称。报告中缺少的字段将为空。

我的问题涉及设计报告的最佳方式和提供给报告的数据,以便报告构建尽可能简单 - 我将允许我的用户生成他们自己的报告 - 我需要两种报告输出- 结果和汇总列表。让我头疼的是聚合。我不仅需要 MIN、MAX、COUNT 等(如 FastReport 内部提供的那样),还需要标准偏差。此外,我想使用 FastReport 的“向下钻取”功能,您可以在其中单击组标题并显示或隐藏数据表。理想情况下,我的聚合应该在页眉中,而不是在页脚中,以便它们始终出现。

我发现 TQuery 中的 SQL 给了我很大的灵活性,因为它提供了“StDev”聚合(FastREport 没有),但据我所知,我的每个字段都需要一个固定的 TQuery。到目前为止,我能想出的最好的解决方案是在主表上使用过滤器“通过”(以便用户可以查看通过、失败或全部),然后用相同的方法构建单独的“统计”表字段名称列,但将 MIN、MAX、COUNT、MEAN、STDEV 作为单独的记录。然后,我将使用 TfrxDBDataSet 将此表公开给 FastReport。我看到我也可以使用 FastReport 自己的 ADODatabase 和 ADOQuery 来直接访问我的 DBF 文件。这很好用,但如果可能的话,我不想在报告中向我的用户公开这个访问层。

当聚合函数必须是基本的数据库要求时,这似乎很混乱。我错过了一种更简单的方法吗?我已经完成了 FastReport(专业)提供的(优秀)演示,并且我正在使用 XE2。如果我需要自己计算 StDev,我也知道 MATH 单元中的有用函数。

我将不胜感激任何指导,谢谢。

4

2 回答 2

2

对于您可以在代码中计算的任何内容、数组值列表、聚合或函数计算结果,我更喜欢使用TfrxUserDataSet并实现TfrxReport.OnGetvalue事件。

尽管最初可能会令人困惑,但用户数据集只是声明了一个数据集名称,以及通过该数据集名称可用的字段列表,并使用触发的事件让您“导航”(第一条,下一条记录)并声明您何时'已达到计算数据的末尾。这允许您构建一个“生成器”,或者只是一个用于计算的普通虚拟数据提供者逻辑集。

这是我的 OnGetValue 事件的样子:

procedure TfrmReport.frxReportGetValue(const VarName: string; var Value: Variant);
begin
   Value := GetReportValue(VarName);
end;

// INPUT:  VarName = '(<GlobalArea."hdReportTitle">)'
// OUTPUT: tableName = 'GlobalArea', fieldName = 'hdReportTitle'
function ParseVar(const VarName:String; var tableName,fieldName:String; var ParenFlag:Boolean):Boolean;
var
 paVarName:String;
 angleBracketFlag:Boolean;
 dotPos:Integer;
 fieldQuoteFlag:Boolean;
 procedure RemoveOuter(var str:String; initialChar,finalChar:Char; var flag);
 var
  n:Integer;
 begin
    n := Length(str);
   if n>2 then begin
      ParenFlag := (str[1]=initialChar) and (str[n]=finalChar);
      if ParenFlag then begin
         str := Copy(str,2,n-2);

      end;
   end;
 end;
begin
   result := false;
   fieldQuoteFlag := false;
   paVarName := SysUtils.Trim(VarName);
   ParenFlag := false;
   tableName := '';
   fieldName := '';
   RemoveOuter(paVarName, '(',')',parenFlag);
   RemoveOuter(paVarName,'<','>',angleBracketFlag);
   dotPos := Pos('.',paVarName);
   if dotPos >0 then begin
    tableName := Copy(paVarName,1,dotPos-1);
    fieldName := Copy(paVarName,dotPos+1,Length(paVarName));
    RemoveOuter(fieldName, '"','"',fieldQuoteFlag);
    result := true;
   end else begin
      tableName := '';
      fieldName := paVarName;
   end;
end;

function TfrmProfitAnalysisReport.GetReportValue(const VarName:String):Variant;
var
 tableName:String;
 fieldName:String;
 parenFlag:Boolean;
begin
 ParseVar(VarName,tableName,fieldName,parenFlag);
 result := NULL;
   { Global Area - Header Values }
 if sameText(tableName,'GlobalArea') then begin
   if fieldName='hdReportTitle' then
      result := GetTitle; { A function that calculates a title for the report }
   else if fieldName='hdReportSubtitle' then
      result := 'Report for Customer XYZ'
   else if fieldName='....' then begin
      ...
   end;

   if Variants.VarIsNull( result) then
      result :=  '?'+fieldName+'?';

end;
于 2012-06-01T19:47:04.077 回答
1

好吧,很多问题都有很多可能的答案:

1)关于数据集,我真的建议将它们放在您的应用程序(DataModule 或 Form)中,而不是在报表中使用它们。它会给你更多的灵活性;

2) 每个聚合可以有一个查询,但是如果您的数据表以大量记录增长,这将影响性能。一些替代方案:

  • 2.1) 计算 FastReport 脚本中的值,但这也会将逻辑暴露给报表;
  • 2.2)遍历Delphi代码上的记录,并将结果作为变量传递给您的报告。例子:

    frxReport.Variables['MIN'] := YourMinVariableOrMethod;
    frxReport.Variables['MAX'] := YourMaxVariableOrMethod;
    
  • 2.3) 使用与您的查询关联的 ClientDataSet 并在 ClientDataSet 上实现 TAggregateFields。

我个人更喜欢 2.2 方法,所有逻辑都在 Delphi 代码中,简单而强大。

于 2012-06-01T16:25:03.743 回答