19

我有一张job桌子

Id
ParentID
jobName
jobStatus

根 ParentID 为 0。

是否可以在 Access 中创建查询以查找给定的根job?数据库是没有链接表的 MDB。Access版本是2003。一个job可以有好几级孙子深。

4

7 回答 7

31

可以在 Access 中创建查询来查找给定作业的根。不要忘记 VBA 函数的强大功能。您可以在 VBA 模块中创建递归函数,并将其结果用作查询中的输出字段。

例子:

Public Function JobRoot(Id As Long, ParentId As Long) As Long
   If ParentId = 0 Then
      JobRoot = Id
      Exit Function
   End If

   Dim Rst As New ADODB.Recordset
   Dim sql As String
   sql = "SELECT Id, ParentID FROM JobTable WHERE Id = " & ParentId & ";"
   Rst.Open sql, CurrentProject.Connection, adOpenKeyset, adLockReadOnly

   If Rst.Fields("ParentID") = 0 Then
      JobRoot = Rst.Fields("Id")
   Else
      JobRoot = JobRoot(Id, Rst.Fields("ParentID"))    ' Recursive.
   End If

   Rst.Close
   Set Rst = Nothing
End Function

您可以通过使用查询构建器或仅在查询字段中输入带有参数的函数名称来从查询中调用此递归函数。

它将产生根。

(我承认 OP 现在已经一岁了,但是当每个人都说不可能的事情是可能的时,我不得不回答)。

于 2010-04-23T18:36:08.730 回答
9

不,不是。在 SServer 2005 之后的 SQL Server 中支持递归查询,但在 Access 中不支持。

如果您事先知道级别数,您可以编写一个查询,但它不会是一个递归查询。

在 SQL Server 中,为此使用 CTE(一个 SQL 扩展):请参阅http://blog.crowe.co.nz/archive/2007/09/06/Microsoft-SQL-Server-2005---CTE-Example- of-a-simple.aspx

但是,常规 SQL 没有递归支持。

于 2009-04-18T06:36:18.207 回答
5

您不能递归查询。

您可以进行任意数量的左连接,但您只能提升与连接数量相同的级别。

或者您可以使用Celko 的“嵌套集模型”来检索所有父母。这将需要修改您的表结构,以使插入和更新更加复杂。

于 2009-04-18T08:24:39.553 回答
3

这不能在 Access 中使用纯 SQL 来完成,但是一点点 VBA 可以走很长一段路。

添加对Microsoft 脚本运行时的引用(工具->引用...)。

这假设 ID 是唯一的,并且没有循环:例如 A 的父节点是 B,但 B 的父节点是 A。

Dim dict As Scripting.Dictionary

Function JobRoot(ID As Long) As Long
    If dict Is Nothing Then
        Set dict = New Scripting.Dictionary
        Dim rs As DAO.Recordset
        Set rs = CurrentDb.OpenRecordset("SELECT ID, ParentID FROM Job", dbOpenForwardOnly, dbReadOnly)
        Do Until rs.EOF
            dict(rs!ID) = rs!ParentID
            rs.MoveNext
        Loop
        Set rs = Nothing

        Dim key As Variant
        For Each key In dict.Keys
            Dim possibleRoot As Integer
            possibleRoot = dict(key)
            Do While dict(possibleRoot) <> 0
                possibleRoot = dict(possibleRoot)
            Loop
            dict(key) = possibleRoot
        Next
    End If
    JobRoot = dict(ID)
End Function

Sub Reset() 'This needs to be called to refresh the data
    Set dict = Nothing
End Sub
于 2015-08-22T22:45:56.837 回答
2

好的,这是真正的交易。首先,您的查询的目标受众是什么……表格?报告?函数/过程?

表格:需要更新吗?在笨拙的情况下使用树视图控件它会很好地工作。报告:在打开事件中使用参数表单设置“老板工作”级别,然后在 vba 中处理递归,并按所需顺序用数据填充记录集。将报告记录集设置为此填充的记录集并处理报告。功能/程序:与上面报告中描述的数据加载几乎相同。通过代码,处理必要的“树行走”并将结果集以所需的顺序存储在记录集中,并根据需要进行处理。

于 2011-07-27T15:42:18.230 回答
1

我在使用树视图结构时遇到了一个相关问题,当用户想要删除一个节点时,他想删除该树下的所有节点。孩子,孩子的孩子等。

递归的工作....

因此,要删除表中的数据以匹配树视图节点删除,请使用将删除节点的递归函数,并向下递归并删除所有子节点、孙节点等。

函数示例:

Public Sub RemoveChildKeys(MyKey)
' deletes passed key and removes all children and grandchildren ect etc of passed key recursively

   Dim TheDB As DAO.Database
   Dim TheTable As DAO.Recordset
   Dim MySql As String

   Set TheDB = CurrentDb

   MySql = "Select * from TblIndex WHERE [Parent]=" & MyKey & ";"
   Set TheTable = TheDB.OpenRecordset(MySql)

   While Not TheTable.EOF
     RemoveChildKeys (TheTable!Key)  ' <---- Calls itself
     TheTable.MoveNext
   Wend

   DoCmd.RunSQL "Delete * FROM TblIndex WHERE [Key]=" & MyKey  ' delete in table

End Sub
于 2019-09-17T01:54:09.087 回答
0

Zev 的贡献给了我很大的启发和学习。但是,需要对代码进行一些编辑。请注意,我的表名为“tblTree”。

Dim dict As Scripting.Dictionary

Function TreeRoot(ID As Long) As Long
    If dict Is Nothing Then
        Set dict = New Scripting.Dictionary ' Requires Microsoft Scripting Runtime
        Dim rs As DAO.Recordset
        Set rs = CurrentDb.OpenRecordset("tblTree", dbOpenForwardOnly, dbReadOnly)
        Do Until rs.EOF
            dict.Add (rs!ID), (rs!ParentID)
            rs.MoveNext
        Loop
        Set rs = Nothing
    End If

    TreeRoot = ID

    Do While dict(TreeRoot) <> 0    ' Note: short version for dict.item(TreeRoot)
        TreeRoot = dict(TreeRoot)
    Loop
End Function

在相同的上下文中还有另一个有用的功能。如果孩子在任何嵌套级别中与提供的 ParentID 匹配,则“ChildHasParent”返回 true。

Function ChildHasParent(ID As Long, ParentID As Long) As Boolean
    If dict Is Nothing Then
        Set dict = New Scripting.Dictionary ' Requires Microsoft Scripting Runtime
        Dim rs As DAO.Recordset
        Set rs = CurrentDb.OpenRecordset("tblTree", dbOpenForwardOnly, dbReadOnly)
        Do Until rs.EOF
            dict.Add (rs!ID), (rs!ParentID)
            rs.MoveNext
        Loop
        Set rs = Nothing
    End If

    ChildHasParent = False

    Do While dict(ID) <> 0    ' Note: short version for dict.item(TreeRoot)
        ID = dict(ID)
        If ID = ParentID Then
            ChildHasParent = True
            Exit Do
        End If
    Loop
End Function
于 2016-06-28T11:13:36.363 回答