Подтвердить что ты не робот

Можно ли создать рекурсивный запрос в Access?

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

Id
ParentID
jobName
jobStatus

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

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

4b9b3361

Ответ 1

Нет, это не так. Рекурсивные запросы поддерживаются в SQL Server после SServer 2005, но не в Access.

Если вы заранее знаете количество рычагов, вы можете написать qry, но это не будет рекурсивным.

В SQL Server для этого используется CTE (расширение SQL): см. http://blog.crowe.co.nz/archive/2007/09/06/Microsoft-SQL-Server-2005---CTE-Example-of-a-simple.aspx

Обычный SQL, однако, не поддерживает рекурсию.

Ответ 2

В 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 уже год, но я вынужден отвечать, когда все говорят, что невозможно).

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

Это невозможно сделать с использованием чистого SQL в Access, но немного VBA проходит долгий путь.

Добавьте ссылку на Время выполнения сценариев Microsoft (Инструменты Ссылки...).

Это предполагает, что идентификатор уникален и что нет циклов: например. Родитель - 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

Ответ 6

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