简体   繁体   English

FDQuery和OnCalcFields,获取上一行

[英]FDQuery and OnCalcFields, get the previous line

Delphi 10.3.3 FireDAC: DBGrid / FDQuery / MySQL VCL Delphi 10.3.3 FireDAC:DBGrid / FDQuery / MySQL VCL

Hi all,大家好,

I have a table with these fields我有一个包含这些字段的表

----------------------
|  id  |    data      |
----------------------
|  1   | 0=A;1=B;2=C  |
|  2   | 2=Z          |
|  3   |              |
|  4   | 0=Y;1=X      |
|  5   |              |
|  6   |              |

Each row of data represents only the change in the table每一行数据只代表表中的变化

I would like this to be display in a DBGRID:我希望它显示在 DBGRID 中:

-----------------------
|  id  | C0 | C1 | C2 |
-----------------------
|  1   | A  | B  | C  |
|  2   | A  | B  | Z  |
|  3   | A  | B  | Z  |
|  4   | Y  | X  | Z  |
|  5   | Y  | X  | Z  |
|  6   | Y  | X  | Z  |

What I can do for now is only the following table:我现在能做的只有下表:

-----------------------
|  id  | C0 | C1 | C2 |
-----------------------
|  1   | A  | B  | C  |
|  2   |    |    | Z  |
|  3   |    |    |    |
|  4   | Y  | X  |    |
|  5   |    |    |    |
|  6   |    |    |    |

To obtain this result, I create additional columns in the event FDQuery1.BeforeOpen And in the event OnCreateFields, I fill each column but I don't know the previous row content ,为了获得这个结果,我在事件 FDQuery1.BeforeOpen 中创建了额外的列,在事件 OnCreateFields 中,我填充了每一列,但我不知道前一行的内容

So, how can I do to fill in the missing fields in the DBgrid?那么,我该如何填写 DBgrid 中缺少的字段呢? Thanks Franck谢谢弗兰克

I think you mean OnCalcFields , rather than OnCreateFields .我认为您的意思是OnCalcFields ,而不是OnCreateFields

What you need is certainly possible, either server-side by deriving the necessary values from the prior row using eg a SQL subquery or client-side using calculated fields.您需要的当然是可能的,无论是服务器端使用例如 SQL 子查询从前一行派生必要的值,还是使用计算字段的客户端。 This answer is about doing it client-side.这个答案是关于在客户端做的。

The problem with doing client-side calculations involving another dataset row is that to do this you need to be able to move the dataset cursor during the OnCalcFields event.进行涉及另一个数据集行的客户端计算的问题是,要做到这一点,您需要能够在 OnCalcFields 事件期间移动数据集 cursor。 However, at the time, the DataSet will be in either dsCalcFields or dsInternalCalc state and, while it is, you can't easily move to another row in the dataset.但是,当时,DataSet 将位于 dsCalcFields 或 dsInternalCalc state 中,尽管如此,但您不能轻易移动到数据集中的另一行。 It is possible to do this, but requires declaring a descendant dataset class (TMyFDQuery) so that you can access the SetTempState necessary to do revert to the prior state after you've picked up the necessary info from the "other" row and, if what you need involves more that one field, you need somewhere to store the values temporarily.可以这样做,但需要声明一个后代数据集 class (TMyFDQuery) 以便您可以访问必要的SetTempState以在您从“其他”行中获取必要的信息后恢复到之前的 state,如果您需要的不仅仅是一个字段,您需要在某个地方临时存储这些值。 So doing it that way gets messy.所以这样做会很混乱。

A much cleaner approach involves using functional similarity between FireDAC's datasets and TClientDataSets.更简洁的方法涉及使用 FireDAC 的数据集和 TClientDataSets 之间的功能相似性。 One of the nice features of TClientDatasSets is the ease with which you can move the dataset contents between two CDSs simply by doing TClientDatasSets 的优点之一是您可以轻松地在两个 CDS 之间移动数据集内容,只需执行以下操作

CDS2.Data := CDS1.Data;

FireDAC datasets can do the same trick, but between any FD dataset types. FireDAC 数据集可以做同样的事情,但在任何 FD 数据集类型之间。 So here is what I would do in your situation:所以这是我在你的情况下会做的:

  1. Add an FDMemTable to your form/datamodule and copy the query data into it in the FDQuery's AfterOpen event like this:将 FDMemTable 添加到您的表单/数据模块并在 FDQuery 的 AfterOpen 事件中将查询数据复制到其中,如下所示:
procedure TForm2.FDQuery1AfterOpen(DataSet: TDataSet);
begin
  FDQuery1.DisableControls;
  try
    FDMemTable1.Data := FDQuery1.Data;
    FDMemTable1.Open;
  finally
    FDQuery1.First;
    FDQuery1.EnableControls;
  end;
end;

The FDQuery1.First is to force it to re-do its calculated fields once the FDMemTable data is available (during the initial FDQuery1.Open, it can't be, of course). FDQuery1.First 是在 FDMemTable 数据可用时强制它重新计算字段(在初始 FDQuery1.Open 期间,当然不能)。

  1. In the FDQuery's OnCalcFields event, use code like this to base the calculated fields' values on values picked up from the prior row (if there is one of course, the first row can't hae a "prior" row):在 FDQuery 的 OnCalcFields 事件中,使用这样的代码将计算字段的值基于从前一行获取的值(当然,如果有一个,第一行不能有“前”行):
procedure TForm2.FDQuery1CalcFields(DataSet: TDataSet);
begin
  if FDMemTable1.Active then begin
    if FDMemTable1.Locate('ContactID', FDQuery1.FieldByName('ContactID').AsInteger, []) then begin
      FDMemTable1.Prior;
      if not FDMemTable1.Bof then begin
        //  Set FDQuery1's calculated fields that depend on prior row
        FDQuery1.FieldByName('PriorRowID').AsInteger := FDMemTable1.FieldByName('ContactID').AsInteger;
      end;
    end;
  end;
end;

In this example, my queried dataset has a ContactID primary key and the calculated value is simply the ContactID value from the prior row.在这个例子中,我查询的数据集有一个ContactID主键,计算的值就是前一行的 ContactID 值。 In real life, of course, it would be more efficient to use persistent field variables rather than keep calling FieldByName .当然,在现实生活中,使用持久字段变量比继续调用FieldByName更有效。

I suppose another possibility might be to use the CloneCursor method to obtain a lookup cursor to access the "prior" row, but I've not tried that myself and it may not be possible anyway (what happens about the calculated fields in the CloneCuror copy?).我想另一种可能性可能是使用 CloneCursor 方法来获取查找 cursor 以访问“先前”行,但是我自己没有尝试过,而且无论如何可能都不可能(CloneCuror 副本中的计算字段会发生什么情况?)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM