在层次结构数据集中获取子记录的递归过程

时间:2016-11-07 06:12:54

标签: delphi recursion

我有一个数据表(MDTasks),它有在树视图中表示的记录。每条记录都有一个唯一字段“ID”和一个字段“Parent_ID”,它指的是父项的ID。我正在尝试列出具有给定ID的记录的任何级别的所有子项。我有以下内容,它在每个级别获得第一个孩子,但不会回到任何级别的兄弟姐妹。我将不胜感激任何帮助。谢谢。

procedure TfmList.GetChildren(TaskID: integer);
var
  iChildID: integer;
begin
  with MDTasks do
  begin
    first;
    while not EOF do
    begin
      if FieldByName('Parent_ID').AsInteger = TaskID then
      begin
        iChildID := FieldByName('ID').AsInteger;
        Memo1.Lines.Add(IntToStr(iChildID));
        GetChildren(iChildID);
      end;
      next;
    end;
  end;
end;

4 个答案:

答案 0 :(得分:2)

如果我理解正确的话,下面的代码应该做你想要的。它使用ClientDataSet,因此我的答案是自包含的,因此可以轻松设置测试数据。

它使用对CloneCursor的调用来对指定父节点ID的子节点进行递归搜索。

procedure TForm1.FormCreate(Sender: TObject);
begin
  CDS1.CreateDataSet;
  CDS1.InsertRecord([1, -1]);
  CDS1.InsertRecord([2, -1]);
  CDS1.InsertRecord([3, -1]);
  CDS1.InsertRecord([4, 2]);  //  This and the following rows are all children of ID = 2 
  CDS1.InsertRecord([5, 2]);
  CDS1.InsertRecord([6, 4]);
  CDS1.InsertRecord([7, 4]);
  CDS1.InsertRecord([8, 7]);

  FindChildren(2);
end;

procedure TForm1.FindChildren(ParentID : Integer);

  procedure FindChildrenInner(ParentID : Integer);
  var
    TempCDS : TClientDataSet;
    ID : Integer;
  begin
    TempCDS := TClientDataSet.Create(Nil);
    try
      TempCDS.CloneCursor(CDS1, False, True);
      TempCDS.First;
      while not TempCDS.Eof do begin
        if TempCDS.FieldByName('Parent_ID').AsInteger = ParentID then begin
          ID := TempCDS.FieldByName('ID').AsInteger;
          Memo1.Lines.Add(Format('ID: %d, Parent: %d', [ID, ParentID]));
          FindChildrenInner(ID);
        end;
        TempCDS.Next;
      end;
    finally
      TempCDS.Free;
    end;
  end;

begin
  Memo1.Lines.BeginUpdate;
  try
    Memo1.Lines.Clear;
    Assert(CDS1.Locate('ID', ParentID, []));
    FindChildrenInner(ParentID);
  finally
    Memo1.Lines.EndUpdate;
  end;
end;

答案 1 :(得分:1)

看一下代码,我会说它让所有的孩子都处于最后一级(不仅仅是第一个)。问题是你"分享"级别之间的数据集(MDTasks) - 所以当最后一级迭代到表的末尾并返回给调用者时,while not EOF do计算为false(现在在父级别)并且循环结束,因此只有第一个孩子的水平高于最后一个孩子。

一种解决方案是完全迭代每个级别,将Parent_ID s记录到本地数组中,然后使用该数组获得它下面的下一级。

答案 2 :(得分:1)

另一种解决方案可能是在表MDTasks中使用书签。让您的过程在开头设置书签并在结尾处返回此书签,以便MDTasks中的位置/光标不会从GetChildren更改。

答案 3 :(得分:-1)

你的问题是你正在针对相同的数据调用递归函数。您可以使用过滤,例如:

procedure GoGoGo (Id: integer);
var
  OldFilter: string;
begin
  OldFilter := Table.Filter;
  Table.Filter := 'ParentId = ' + IntToStr (Id);
  try
    Table.First;
    while not Table.Eof do begin
      Memo.Lines.Add (Format('ID: %d, Parent: %d', 
        [Table.FieldByName ('I'd).AsInteger, 
         Table.FieldByName ('ParentID').AsInteger]));
      GoGoGo (Table.FieldByName ('Id').AsInteger);
      if Table.Locate ('Id', Id, []) then
        Table.Next;
    end;
  finally
    Table.Filter := OldFilter;
  end;
end;

<...>
Memo.Lines.Clear;
Table.Filtered := TRUE;
Table.First;
GoGoGo (Table.FieldByName ('Id').AsInteger);

没有测试过这段代码,它可能包含错误,但希望很明显。使用相同的想法,你可以使用另一种方法,比如用动态生成的where子句创建新的TQuery等等。我不是因为在一个非常大的树中,在递归函数的每次迭代中创建新对象都不喜欢这样的东西。可能导致堆栈溢出。

gl那里;)