【问题标题】:How can I implement "writable calculated" fields in a TDataSet?如何在 TDataSet 中实现“可写计算”字段?
【发布时间】:2011-07-18 02:49:44
【问题描述】:

我需要向 TDataSet 添加其他字段,这些字段在基础数据库中不存在,但可以从现有字段派生。我可以使用 caclulated 字段轻松地做到这一点,并且效果很好。

现在我想编辑这些字段并将更改的数据写回。我可以反转计算以将数据写回现有字段,但 DB 控件不允许我编辑计算字段。

有什么方法可以让我这样做吗?

更新: 好的,关于背景的更多细节。

数据集有一个 blob 字段,它是一个 TBytes 表示。一些字节被标识为包含可以用现有 DB 编辑字段以方便的方式表示的信息。但是,并非所有字节都是已知的,因此 TBytes 表示必须保持原样,以便通过另一个知道它的应用程序进行处理。此应用还可以修改现有记录并插入新记录。

数据集中不同记录的 TBytes 通常映射到不同的字段表示,尽管在数据集上设置过滤器或范围将确保它们具有相同的映射。

正如我所说,提取已知字节并通过计算字段将其转换为字符串、日期、数字等是没有问题的。也可以将这些值重新转换为 TBytes。问题在于使这些额外的字段可编辑,同时保持数据集导航不变。

如果有帮助:我们有执行双向映射的类,将字段公开为已发布的属性。

【问题讨论】:

  • 计算字段按定义是只读的(嗯,在 CalcFields 阶段它们不是)。请更详细地说明您的目标。
  • 您是否愿意或已经在使用 TClientDataSets?
  • 我愿意接受任何事情。仍在调查其他建议,但还不满意。

标签: delphi dataset calculated-field


【解决方案1】:

我认为计算字段根据定义是只读的,其值是在客户端计算的。 您想要的可能可以通过可更新的视图来实现。您已经可以使用计算字段定义视图 - 这些将在服务器上的 SQL 中计算 - 以及一个更新触发器,也可能是一个插入触发器 - 以执行反向计算。然后从客户端您可以像表格一样透明地使用视图。

【讨论】:

  • 我知道计算字段是只读的,因此是引号。问题是这些字段不能用SQL计算,也不能在服务器上计算。
  • 有趣,为什么不呢?如果你因为某种原因不能使用 SQL,你就不能写一个 UDF 吗?
  • 也许另一种选择是将客户端数据集 + 数据集提供程序与 ApplyUpdates 处理程序一起使用。
  • 如何让提供者公开那些不属于原始数据集的字段?你有例子吗?
  • 对不起,我手头没有任何示例,但正如 Fabricio 所说,您可以使用 OnGetRecords 在将数据包发送到客户端之前对其进行修改,并且我认为您可以使用相反的方向OnBeforeApplyUpdates。但是关于您的更新,我有 UDF,例如解压缩 blob 或解析 XML 并返回某些值,因此可以简单地在 SQL 语句中调用 UDF。
【解决方案2】:

我在 ClientDataSet 上遇到了类似的问题,我已经使用 SQL-Stmt 上的虚拟文件解决了这个问题,因此我可以模拟数据库中的字段。

见我的Question

【讨论】:

    【解决方案3】:

    您可以使用 TDatasetProvider.OnGetRecords(不记得这是否是事件的正确名称)并修改发送到 Clientdataset 的数据包。

    当然,正如 TOndrej 所说,您必须在 ApplyUpdates 处理程序上处理它们。

    【讨论】:

      【解决方案4】:

      Woll2Woll 的 infopower 组件(我刚刚测试了他们的 TwwDBEdit)允许这样做。所以我会认为你在 TDBEdit 级别(或 TFieldDataLink 级别)的任何块。

      TWWDBETIT的既既既既有区别,我不知道。 (而且我不确定许可协议是否允许我在这里发帖……)。

      【讨论】:

        【解决方案5】:

        答案取决于您使用的数据访问组件。我使用的是 Anydac,它支持 fkInternalCalc 字段,可以按照手动编辑的方式计算。

        【讨论】:

        • 数据库是本地的,所以我宁愿避免任何不是绝对需要的层。但我认为,对 fkInternalCalc 字段的提示让我走上了正确的道路。
        • 很难决定接受哪个答案,但 fkInternalCalc 字段的提示确实将我推向了正确的方向。
        【解决方案6】:

        在我们的数据库设计中,一些值是相对于另一列的百分比(下面称为oMean),而其他浮点值存储为绝对值。我们的客户后来想要所有字段的两个选项(rel. 和 abs.),所以我们想出了以下从 TFloatField 派生的类。它应该适用于所有 TDataSet 后代。

        unit EditableCalcFloatField;
        
        interface
        
        uses
          db, classes;
        
        type
          TEditableCalcFloatField = class(TFloatField)
          public
            oAbs, oRel, oMean: TField;
          protected
            function GetCanModify: Boolean; override;
            procedure SetAsFloat(Value: Double); override;
          end;
        
        implementation
        
        function TEditableCalcFloatField.GetCanModify: Boolean;
        begin
          Result := oMean.AsFloat <> 0;
          if not Result then Exit;
          Result := (oAbs <> nil) or (oRel <> nil);
        end;
        
        procedure TEditableCalcFloatField.SetAsFloat(Value: Double);
        var
          fMean                                 : Double;
        begin
          inherited;
        
          if DataSet.State in [dsEdit, dsInsert] then begin
            fMean := oMean.AsFloat;
            if fMean = 0 then Exit;
            if oAbs <> nil then
              oAbs.AsFloat := Value / 100 * fMean
            else
              oRel.AsFloat := Value / fMean * 100;
          end;
        end;
        
        end.
        

        要在没有包的情况下使用它,您必须在打开数据集之前在FormCreate 中创建字段:

        with TEditableCalcFloatField.Create(Self) do
        begin
          oAbs := sqlMerkmaleYourAbsoluteColumn;
          DisplayLabel := sDisp;
          oMean := sqlMerkmaleAssignedValue_Mean;
          Calculated := True;
          FieldName := 'R' + oAbs.FieldName;
          DataSet := sqlMerkmale;
        end;
        

        当然,它的内容可以在OnCalcFields 事件中设置,也可以由用户设置。

        【讨论】:

        • 我不明白SetAsFloat 中的布尔逻辑:if (fMean = 0) and (fMean = -999) then Exit;...` 你的意思是or
        • 是的,你没看错,应该是or。感谢您指出这一点。
        • 好主意,谢谢!我只想添加一件事:SetAsFloat 中的处理应该被设置为 false 的 AutoCalcFields 包围,然后再恢复。否则,在 dsEdit 状态下,如果您更改此字段以及随后链接的字段(oAbs、oRel),则会触发计算字段的计算,该计算字段很可能会递归回设置此字段。那么,它会无限期地递归。
        【解决方案7】:

        使用'select *,0的myquery)使用tquery descendant(myquery)从mytable'

        Procedure MyQueryAfterOpen(Dataset:TDataSet);
        Begin
            DataSet.FieldByName('TempField').ReadOnly := False;
        End;
        

        现在是editabe临时字段

        【讨论】:

          猜你喜欢
          • 2021-03-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多