Можно ли создать рекурсивный запрос в Access? - PullRequest
18 голосов
/ 18 апреля 2009

У меня есть job таблица

Id
ParentID
jobName
jobStatus

Корень ParentID равен 0.

Возможно ли в Access создать запрос для поиска корня для данного job? База данных MDB без связанных таблиц. Версия Access - 2003. A job может иметь несколько уровней внуков.

Ответы [ 6 ]

25 голосов
/ 23 апреля 2010

В 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

Вы можете вызвать эту рекурсивную функцию из вашего запроса, используя построитель запросов или просто введя имя функции с аргументами в поле запроса.

Это даст рут.

(я признаю, что ФП уже год, но я вынужден ответить, когда все скажут, что невозможное возможно).

8 голосов
/ 18 апреля 2009

Нет, это не так. Рекурсивные запросы поддерживаются в 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, однако, не поддерживает рекурсивность.

4 голосов
/ 18 апреля 2009

Вы не можете рекурсивно запрашивать.

Вы можете сделать произвольное количество левых соединений, но вы сможете пройти только столько уровней, сколько у вас есть соединений.

Или вы можете использовать Celko "Модель вложенного набора" , чтобы найти всех родителей. Это потребует изменения структуры таблицы таким образом, чтобы сделать вставки и обновления более сложными.

2 голосов
/ 23 августа 2015

Это не может быть сделано с использованием чистого SQL в Access, но немного VBA имеет большое значение.

Добавить ссылку на Microsoft Scripting Runtime ( Инструменты -> Ссылки ... ).

Предполагается, что идентификатор является уникальным и что циклов нет: например, Родитель 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
1 голос
/ 27 июля 2011

ОК, так вот РЕАЛЬНАЯ сделка. Во-первых, какова целевая аудитория для вашего запроса ... форма? отчет? Функция / прок?

Форма: нужны обновления? используйте элемент управления treeview, в то время как неуклюжий он будет работать хорошо. Отчет: в событии open используйте форму параметра, чтобы установить уровень «Boss Job», затем обработайте рекурсию в vba и заполните набор записей данными в требуемом порядке . установите набор отчетов для этого заполненного набора записей и обработайте отчет. Функция / Процедура: работает почти так же, как загрузка данных, описанная в отчете выше. С помощью кода обработайте необходимую «прогулку по дереву» и сохраните набор результатов в нужном порядке в наборе записей и обработайте при необходимости.

0 голосов
/ 28 июня 2016

Вклад Зев дал мне много вдохновения и обучения. Однако необходимо внести некоторые изменения в код. Обратите внимание, что моя таблица называется "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

И есть еще одна полезная функция в том же контексте. ChildHasParent возвращает true, если дочерний элемент соответствует указанному ParentID на любом уровне вложенности.

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
...