简体   繁体   中英

FDQuery and OnCalcFields, get the previous line

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:

-----------------------
|  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 ,

So, how can I do to fill in the missing fields in the DBgrid? Thanks Franck

I think you mean OnCalcFields , rather than 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. 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. 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. 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. So doing it that way gets messy.

A much cleaner approach involves using functional similarity between FireDAC's datasets and 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

CDS2.Data := CDS1.Data;

FireDAC datasets can do the same trick, but between any FD dataset types. 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:
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).

  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):
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. In real life, of course, it would be more efficient to use persistent field variables rather than keep calling 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?).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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