【问题标题】:How best to present my Delphi database table to FastReport for lists and aggregates如何最好地将我的 Delphi 数据库表呈现给 FastReport 以获取列表和聚合
【发布时间】:2012-06-02 03:47:04
【问题描述】:

到目前为止,我已经花了几天时间为在我的应用程序中使用 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 单元中的有用函数。

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

【问题讨论】:

    标签: delphi aggregate-functions fastreport


    【解决方案1】:

    对于您可以在代码中计算的任何内容、数组值列表、聚合或函数计算结果,我更喜欢使用 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;
    

    【讨论】:

    • +1 记住 TfrxUserDataSet 方法,对多变量和计算非常有用。
    • 这是一个非常干净的解决方案。谢谢
    • 我应该展示myuserdataset.RecNo的用法——这是我的演示中缺少的一件事......数据集的recno是你如何找出哪个值,带状(逐行) 报告的部分,是需要的。
    • 'recno'如何连接到乐队?
    【解决方案2】:

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

    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 代码中,简单而强大。

    【讨论】:

    • 2.2:这可能有很多变数。因此,我建议改为使用 OnGetvalue 事件处理。
    猜你喜欢
    • 1970-01-01
    • 2019-05-05
    • 1970-01-01
    • 2020-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-15
    相关资源
    最近更新 更多