获取父母的所有可能的孩子/后代
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,我会调查的。以上是关于获取父母的所有可能的孩子/后代的主要内容,如果未能解决你的问题,请参考以下文章