4

I have the following code which traverse all data in the TClientDataSet, my purpose is to delete all records except DocKey=20381.

But with the following codes, you will notice record with DocKey=20381 being traversed twice (traverse times = 6, which suppose to be 5 times as we only have 5 records in TClientDataSet).

If we enable this line -> D.IndexFieldNames := 'DocKey', then the data will traverse correctly. May I know is this a Delphi bug? Or any way to solve this besides using IndexFieldNames?

var
  D: TClientDataSet;
begin
  D := TClientDataSet.Create(Self);
  with D do begin
    FieldDefs.Add('DocKey', ftInteger);
    CreateDataSet;
    AppendRecord([20157]);
    AppendRecord([20162]);
    AppendRecord([20381]);
    AppendRecord([20372]);
    AppendRecord([20377]);
  end;
  // D.IndexFieldNames := 'DocKey';

  D.First;
  while not D.Eof do begin
    if D.Fields[0].AsInteger = 20381 then
      D.Next
    else
      D.Delete;
  end;
end;
Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
Sherlyn Chew
  • 189
  • 1
  • 2
  • 8

1 Answers1

5

This behavior is as designed and documented

If the record deleted was the last record in the dataset, then the previous record becomes the current record.

20157 -> delete
20162 -> delete
20381 -> next
20372 -> delete
20377 -> delete (last record -> goto previous record -> not eof)

With this line

D.IndexFieldNames := 'DocKey';

the not to be deleted record becomes the last record in dataset and therefore there is no delete at the last record and not that behavior.

20157 -> delete
20162 -> delete
20372 -> delete
20377 -> delete
20381 -> next

UPDATE

If you want to avoid this - for any reasons I did not know or I could imagine - just check, if the current RecNo is decreased.

var 
  D: TClientDataSet;
  LRecNo : Integer;
begin
  D := TClientDataSet.Create(Self);
  with D do begin
    FieldDefs.Add('DocKey', ftInteger);
    CreateDataSet;
    AppendRecord([20157]);
    AppendRecord([20162]);
    AppendRecord([20381]);
    AppendRecord([20372]);
    AppendRecord([20377]);
  end;
  // D.IndexFieldNames := 'DocKey';

  D.First;
  while not D.Eof do 
  begin

    LRecNo := D.RecNo;

    if D.Fields[0].AsInteger = 20381 then
      D.Next
    else
      D.Delete;

    if LRecNo > D.RecNo then
      Break;

  end;
end;
Sir Rufo
  • 18,395
  • 2
  • 39
  • 73
  • Thanks for your reply. Is that the way of writing the for-loop codes is not right or any better way of writing to avoid the problem occurred? Should i add bookmark into a TList (for those records to be deleted) first instead of straight call TClientDataSet.Delete in the for-loop, and delete records based on the TList after that. – Sherlyn Chew May 09 '14 at 07:27
  • Why is this a *problem*? – Sir Rufo May 09 '14 at 07:30
  • I actually expect the dataset to traverse next record like this 20377 -> delete (last record -> next (no more record) -> eof) – Sherlyn Chew May 09 '14 at 07:31
  • But then your expectation is the only problem? It happens only one time and all records are deleted as expected. I did not get the real problem – Sir Rufo May 09 '14 at 07:34
  • Thanks for your update. I have some code that traverse the dataset to perform arithmetic calculation which each records should be only traverse once and not twice, to avoid incorrect calculation. At the same time delete unwanted master record if the detail dataset.recordcount = 0 (master and detail dataset linking) – Sherlyn Chew May 12 '14 at 01:40