是否可以在Access中创建递归查询?

时间:2009-04-18 06:24:06

标签: ms-access recursion hierarchy

我有job

Id
ParentID
jobName
jobStatus

根ParentID为0。

是否可以在Access中创建查询以查找给定job的根? 数据库是MDB,没有链接表。 Access版本是2003. job可以是几个级别的大孩子。

7 个答案:

答案 0 :(得分:25)

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现在已经有一年了,但是当每个人都说出不可能的事情时,我不得不回答。)

答案 1 :(得分:8)

不,不是。 SQL Server在SServer 2005之后支持递归查询,但在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没有Recursivity支持。

答案 2 :(得分:4)

您不能以递归方式查询。

你可以做一些任意数量的左连接,但是你只能加入你加入的连接数。

或者您可以使用Celko's "Nested Set Model"检索所有父母。这将需要修改表结构,使插入和更新更加复杂。

答案 3 :(得分:2)

在Access中使用纯SQL无法做到这一点,但是一点VBA还有很长的路要走。

添加对 Microsoft Scripting Runtime 的引用(工具 - > 引用... )。

这假设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

答案 4 :(得分:1)

好的,所以这是真正的交易。首先,您的查询的目标受众是什么?表单?报告?功能的/ proc?

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

答案 5 :(得分:0)

Zev的贡献给了我很多灵感和学习。但是,需要对代码进行一些编辑。请注意,我的表名为&#34; tblTree&#34;。

{{1}}

在同一个环境中还有另一个有用的功能。 &#34; ChildHasParent&#34;如果子项与任何嵌套级别中提供的ParentID匹配,则返回true。

{{1}}

答案 6 :(得分:0)

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

递归工作。...

因此,要删除表中的数据以匹配Treeview节点的删除,请使用递归函数,该函数将删除该节点,然后递归并删除所有子节点,孙子节点等。

函数示例:

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