获取父母的所有可能的孩子/后代

Posted

技术标签:

【中文标题】获取父母的所有可能的孩子/后代【英文标题】:Get all possible childs/decendants of a parent 【发布时间】:2020-11-29 10:47:20 【问题描述】:

我对此进行了很多搜索,但无法找到任何可用于 MS Access 的东西,我找到了 SQL 的解决方案,但访问 SQL 中不允许使用的语句。

因此,在 MS Access 2019 中,我有一个带有 Id 和 ParentID 的表 tbContentList。我想要的是显示特定父母的所有孩子/后代。

我的桌子是这样的:

如果我想显示 ID 3 的所有孩子,我希望得到结果:

这在 MS 访问查询中是否可行?使用 VBA 是可能的,但我认为使用查询更快。有人可以帮我解决这个问题吗?

SQL 等价物: https://www.codeproject.com/Articles/818694/SQL-Queries-to-Manage-Hierarchical-or-Parent-child (所有可能的孩子)

【问题讨论】:

嗨。当您说 ID 3 的所有孩子时,您是指 ParentID 为 3 的所有记录吗?如果是这样,您的示例结果没有显示这一点,如果没有,您究竟是什么意思? 请更新您的问题以包含您已链接到的图像。更好的是,对于源数据,将实际文本放入问题中,以便人们可以剪切和粘贴而不是重新输入。 这能回答你的问题吗? Is it possible to create a recursive query in Access?。你想要3的所有后代?这对于 Access 来说并不容易,仅使用 SQL 来完成几乎是不可能的。需要VBA。另请查看codeproject.com/questions/338969/… 和fabalou.com/Access/General/genealogy_example.asp。我相信您可以找到更多示例 Access dbs。 大家好,感谢您的快速反馈。不幸的是,如果您是新手,则不允许包含图像。我会尽可能更新。 【参考方案1】:

所以,我能够从Gustav 修改逻辑并使其适合我的项目。我将父结果放在分隔符“;”之间。这使得在查询中查找特定 ContentID 的后代变得更加容易。此外,由于某些 ContentID 是树的开头,因此我必须处理父列中的 Null 值。

Public Function GetParentIDs(ByVal lContentID As Long) As String
    Static dbs As DAO.Database
    Static tbl As DAO.TableDef
    Static rst As DAO.Recordset
    Dim strParents As String

    If dbs Is Nothing Then
        ' For testing only.
        ' Replace with OpenDatabase of backend database file.
        Set dbs = CurrentDb
        Set tbl = dbs.TableDefs("tbContentList")
        Set rst = dbs.OpenRecordset(tbl.Name, dbOpenTable)
    End If

    With rst
        .Index = "PrimaryKey"
        Do While lContentID > 0
            .Seek "=", lContentID
            If Not .NoMatch Then
                lContentID = Nz(!ParentID.Value, 0)
                If lContentID > 0 Then
                    strParents = ";" & CStr(lContentID) & strParents
                Else
                    Exit Do
                End If
            Else
                Exit Do
            End If
        Loop
        ' Leave recordset open.
        ' .Close
    End With

    '  Don't terminate static objects.
    '  Set rst = Nothing
    '  Set tbl = Nothing
    '  Set dbs = Nothing

    'Return value
    If strParents = "" Then
        GetParentIDs = ""
    Else
        GetParentIDs = strParents & ";"
    End If
End Function

从特定 ContentID 获取所有后代的查询。本例中的 3 if 可以更改为另一个值。

SELECT tbContentList.[ContentID], tbContentList.[ParentID], tbContentList.[Item], GetParentIDs([ContentID]) AS Parents
FROM tbContentList
WHERE (((GetParentIDs([ContentID])) Like '*;3;*'));

感谢您的帮助,让我朝着正确的方向前进。

【讨论】:

【参考方案2】:

您有多种选择。然而,一个是不行的,那就是只使用 SQL 的递归查询。访问不能被愚弄,并且会声称循环引用。您唯一的机会是创建一个仅解决有限数量级别的查询,例如 8 或 10。

但是您可以在 DLookup() 等域聚合函数中覆盖递归调用。但是,这非常慢,因为调用查询的 DLookup() 将为每条记录运行。对于数十条以上的记录,这很可能是不可接受的。

对于无限数量的级别,我发现最快的方法是创建一个查找函数,该函数遍历每个记录的树。这可以输出记录的级别或由记录的键和上面的所有键构建的复合键。

由于查找功能将为每次调用使用相同的记录集,您可以将其设为静态,并且(对于 Jet)您可以通过使用 Seek 来定位记录来进一步改进。

这里有一个类似案例的例子,它会给你一个想法:

Public Function RecursiveLookup(ByVal lngID As Long) As String

    Static dbs      As DAO.Database
    Static tbl      As DAO.TableDef
    Static rst      As DAO.Recordset

    Dim lngLevel    As Long
    Dim strAccount  As String

    If dbs Is Nothing Then
        ' For testing only.
        ' Replace with OpenDatabase of backend database file.
        Set dbs = CurrentDb
        Set tbl = dbs.TableDefs("tblAccount")
        Set rst = dbs.OpenRecordset(tbl.Name, dbOpenTable)
    End If

    With rst
        .Index = "PrimaryKey"
        While lngID > 0
            .Seek "=", lngID
            If Not .NoMatch Then
                lngLevel = lngLevel + 1
                lngID = !MasterAccountFK.Value
                If lngID > 0 Then
                    strAccount = str(!AccountID) & strAccount
                End If
            Else
                lngID = 0
            End If
        Wend
        ' Leave recordset open.
        ' .Close
    End With

    '  Don't terminate static objects.
    '  Set rst = Nothing
    '  Set tbl = Nothing
    '  Set dbs = Nothing

    '  Alternative expression for returning the level.
    '  (Adjust vartype of return value of function.)
    '  RecursiveLookup = lngLevel ' As Long
    RecursiveLookup = strAccount

End Function

这假设一个表具有一个主键 ID 和一个指向父记录的外(主)键 - 以及一个可见键 (AccountID) 为 0 的***记录(未使用)。

现在您的树几乎可以立即使用这样的查询很好地显示出来,其中 Account 将是可见的复合键:

SELECT
    *, RecursiveLookup([ID]) AS Account
FROM
    tblAccount
WHERE
    (AccountID > 0)
ORDER BY
    RecursiveLookup([ID]);

【讨论】:

谢谢 Gustav,我会调查的。

以上是关于获取父母的所有可能的孩子/后代的主要内容,如果未能解决你的问题,请参考以下文章

使用谓词在 Core Data 中获取父母的所有孩子

Qt:获取父母区域内的所有孩子

Mysql如何获取给定ID的父母和所有孩子? [复制]

Swift - CoreData NSPredicate - 获取父母的孩子

使用cheerio在没有孩子的父母中获取文本

Diesel 获取单亲和许多孩子 - 火箭