【问题标题】:Looping to find related records循环查找相关记录
【发布时间】:2020-02-04 22:33:36
【问题描述】:

在设置循环时寻求帮助/指导?函数在表中查找相关记录。

表 (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 |
+---+-------+-------+

【问题讨论】:

    标签: sql vba loops ms-access


    【解决方案1】:

    您关于查询自引用数据的问题与this question 非常相似,其中用户有一个员工表,每个员工可能有一个主管,其员工记录也存在于同一个表中,从而形成层次结构.

    解决此问题的相对简单的方法是在循环或递归调用中使用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 
    

    这当然只是打印事务 ID,但如果需要,该函数也可以使用每个事务的数据填充单独的表。


    但是,如果我们可以构造一个 SQL 查询一次性返回所有结果,那么逐条返回结果或填充临时表似乎并不优雅。

    但是,由于 MS Access 不支持递归 SQL 查询,查询此类分层数据时的困难在于不知道要提前编写多少级代码。

    因此,您可以使用 VBA 函数来构建 SQL 查询本身,从而始终合并尽可能多的级别以返回完整的数据集。

    确实,这是我在my answer 中针对上面链接的相关问题提出的方法 - 该答案中提供的功能同样可以适应这种情况,例如:

    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
    

    现在,评估事务 ID 为 4BuildQuerySQL 函数会生成以下 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
    

    因此可以评估此​​类函数以构造保存的查询,例如对于事务 ID = 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 |
    +---------+---------+---------+--------+
    

    【讨论】:

    • 谢谢@lee-mac,我确实看到了那个帖子,但认为它不适用于我的具体情况。此建议返回 3 个级别,但如果我不知道到达顶部需要多少级别(序列中的第一个事务)怎么办?
    • @Ember 您是否完整阅读了我的帖子?提供的 VBA 函数将处理任意数量的级别。
    • 是的,我确实阅读了整本书,实际上多次阅读,但显然我并没有完全理解。我将您建议的 BuildQuerySQL 添加到新模块中,并将 SQLquery 添加到新查询中。我没有看到这两者是如何相互关联的,但是当我运行我的新查询(使用您建议的代码)时,它起作用了,但它只显示了 3 个级别。目前在我的数据库中,我有一个 5 级链。我想出了如何嵌套内部连接,现在当我运行查询时,我确实看到了我的 5 级链。
    • 在重新阅读所有内容时,我认为建议的 querySQL 并不是要直接添加到查询中,而是在引用 BuildQuerySQL 函数后将添加的内容。我添加了一个命令按钮,并在 OnClick 上添加了一个事件过程来调用 docmd.openmodule“TransLookup”、“BuildQuerySQL”。 TransLookup 是我所说的模块,因为它提供了几个功能。当我点击我的命令按钮时,它只是将我带到模块,但没有弹出错误......我认为我仍然缺少一块。搜索继续……
    • 非常感谢您的帮助!该功能运行良好!
    猜你喜欢
    • 1970-01-01
    • 2013-01-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-19
    • 2012-02-02
    • 1970-01-01
    相关资源
    最近更新 更多