Цикл поиска соответствующих записей - PullRequest
1 голос
/ 07 октября 2019

Ищете помощь / направление в настройке петли? функция для поиска связанных записей в таблице.

Таблица (tblTransactions) содержит информацию о различных отслеживаемых транзакциях. Я также использую эту таблицу для ссылки на предшествующую транзакцию. Теперь я ищу способ перебрать таблицу, чтобы найти связанные записи.

В таблице есть следующие поля:

  • TransID - первичный ключ

  • Grantor - поле имени

  • Grantee - поле имени

  • PTrans - номерполе, которое ссылается на TransID


Некоторые примеры данных:

+---------+---------+---------+--------+
| TransID | Grantor | Grantee | PTrans |
+---------+---------+---------+--------+
|       1 | Bob     | Sally   |      0 |
|       2 | Jane    | Emily   |      0 |
|       3 | Sally   | Beth    |      1 |
|       4 | Beth    | Sam     |      3 |
+---------+---------+---------+--------+

В идеале я хотел бы иметь возможность начать с TransID 4 и показать все данные транзакции в отдельных строках для выбранной транзакции (4) и ее предшественников.

Результаты будут такими:

+---+-------+-------+
| 4 | Beth  | Sam   |
| 3 | Sally | Beth  |
| 1 | Bob   | Sally |
+---+-------+-------+

1 Ответ

0 голосов
/ 07 октября 2019

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

Относительно простым способом решения этой проблемы было бы использование выражения DLookup внутри цикла или внутри рекурсивного вызова до тех пор, пока выражение не вернет Null. Например, вот рекурсивный вариант:

Function TransLookup(lngtrn As Long)
    Dim lngptr
    lngptr = DLookup("ptrans", "tbltransactions", "transid = " & lngtrn)

    If Not IsNull(lngptr) Then
        Debug.Print lngtrn ' Do something with the data
        TransLookup (lngptr)
    End If
End Function

При оценке с вашими данными это даст:

?TransLookup(4)
 4 
 3 
 1 

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


Однако возврат результатов запись за записью или заполнение временной таблицы кажется неэффективным, если мы можем создать один запрос SQL, чтобы вернуть всерезультатов за один раз.

Однако, поскольку MS Access не поддерживает рекурсивные SQL-запросы, сложность при запросе таких иерархических данных заключается в том, что они не знают, сколько уровней нужно кодировать заранее.

Таким образом, вы можете использовать функцию VBA для построения самого запроса SQL, и, таким образом, всегда включать столько уровней, сколько необходимо для возврата полного набора данных.

Действительно, этот подход я выдвинул в мой ответ на связанный вопрос, связанный выше - функция, представленная в этом ответе, может бытьможет быть адаптирован к этой ситуации, например:

Function BuildQuerySQL(lngtrn As Long) As String
    Dim intlvl As Integer
    Dim strsel As String: strsel = selsql(intlvl)
    Dim strfrm As String: strfrm = "tbltransactions as t0 "
    Dim strwhr As String: strwhr = "where t0.transid = " & lngtrn

    While HasRecordsP(strsel & strfrm & strwhr)
        intlvl = intlvl + 1
        BuildQuerySQL = BuildQuerySQL & " union " & strsel & strfrm & strwhr
        strsel = selsql(intlvl)
        If intlvl > 1 Then
            strfrm = "(" & strfrm & ")" & frmsql(intlvl)
        Else
            strfrm = strfrm & frmsql(intlvl)
        End If
    Wend
    BuildQuerySQL = Mid(BuildQuerySQL, 8)
End Function

Function HasRecordsP(strSQL As String) As Boolean
    Dim dbs As DAO.Database
    Set dbs = CurrentDb
    With dbs.OpenRecordset(strSQL)
        HasRecordsP = Not .EOF
        .Close
    End With
    Set dbs = Nothing
End Function

Function selsql(intlvl As Integer) As String
    selsql = "select t" & intlvl & ".* from "
End Function

Function frmsql(intlvl As Integer) As String
    frmsql = " inner join tbltransactions as t" & intlvl & " on t" & intlvl - 1 & ".ptrans = t" & intlvl & ".transid "
End Function

Теперь, оценка функции BuildQuerySQL с идентификатором транзакции 4 дает следующий запрос SQL UNION, где каждый уровень вложенности объединяется спредыдущий запрос:

select 
    t0.* 
from 
    tbltransactions as t0 
where 
    t0.transid = 4 

union 

select 
    t1.* 
from 
    tbltransactions as t0 inner join tbltransactions as t1 
    on t0.ptrans = t1.transid 
where 
    t0.transid = 4 

union 

select 
    t2.* 
from
    (
        tbltransactions as t0 inner join tbltransactions as t1 
        on t0.ptrans = t1.transid 
    ) 
    inner join tbltransactions as t2 
    on t1.ptrans = t2.transid 
where 
    t0.transid = 4

Поэтому такую ​​функцию можно оценить для построения сохраненного запроса, например, для идентификатора транзакции = 4, следующее создаст запрос с именем TransactionList:

Sub test()
    CurrentDb.CreateQueryDef "TransactionList", BuildQuerySQL(4)
End Sub

Или, альтернативно, SQL может быть оценен, чтобы открыть RecordSet результатов, в зависимости от требований вашего приложения.

При оценке с вашими примерами данных вышеприведенный SQL-запрос даст следующие результаты:

+---------+---------+---------+--------+
| TransID | Grantor | Grantee | PTrans |
+---------+---------+---------+--------+
|       1 | Bob     | Sally   |      0 |
|       3 | Sally   | Beth    |      1 |
|       4 | Beth    | Sam     |      3 |
+---------+---------+---------+--------+
...