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:
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).
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.