如何在 VBA 到 return 列名称中为记录集中的每条记录创建与特定条件匹配的函数?

How to create a function in VBA to return column names matching a certain criteria for each record in a recordset?

我有一个 table,其中包含对调查的回复。例如。, 表 A:

CompanyID   Q1  Q2  Q3  Q4  Q5
CompanyA    I   I   N   N   I
CompanyB    I   I   I   I   I
CompanyC    I   I   N   N   N

我正在使用 MS-Access 2016。我想创建一个 VBA 函数,让我可以遍历 table 和 return [=31] 中的每条记录=] 其中对问题的回答是 "N",用逗号 (,) 分隔。

请记住我绝不是专家,也没有接受过任何正规培训。老实说,我的大部分 VBA 都是通过这个论坛学习的。感谢所有为这个社区提供意见的人。

到目前为止,我能够 VBA 循环遍历每条记录,但我 运行 遇到了几个问题,请参阅下面的代码:

Public Function NResponses(strTable As String)

On Error GoTo Err_Handler

    Dim rs As DAO.Recordset      
    Dim fld As DAO.Field          
    Dim strOut As String            
    Dim lngLen As Long                     
    Dim strSeperator As String      

NResponses = Null

Set dbs = CurrentDb
Set rs = dbs.OpenRecordset("TableA")
strSeperator = ", "

Do While Not rs.EOF
    With rs
        For Each fld In .Fields
            If fld.Value = "N" Then
                strOut = strOut & fld.Name & strSeperator
            End If
        Next fld
        rs.MoveNext
    End With
Loop

rs.Close
Set rs = Nothing

'Clean Output - remove last comma from strOut
lngLen = Len(strOut) - Len(strSeperator)
    If lngLen > 0 Then
        MissingControls = Left(strOut, lngLen)
    End If

Exit_Handler:
    'Clean up
    Set rs = Nothing
    Exit Function

Err_Handler:
    MsgBox "Error " & Err.Number & ": " & Err.Description, vbExclamation, "NResponses()"
    Resume Exit_Handler
End Function

这 return 如下:

CompanyID   Q1  Q2  Q3  Q4  Q5  NResponses
CompanyA    I   I   N   N   I   Q1, Q3, Q4, Q5
CompanyB    I   I   I   I   I   Q1, Q3, Q4, Q5
CompanyC    N   I   I   N   N   Q1, Q3, Q4, Q5

但是,我想要的最终结果是这样的:

CompanyID   Q1  Q2  Q3  Q4  Q5  NResponses
CompanyA    I   I   N   N   I   Q3, Q4
CompanyB    I   I   I   I   I   
CompanyC    N   I   I   N   N   Q1, Q4, Q5

我们将不胜感激。

你好,strOut是字符串,必须是数组。

试试这样的东西(未经测试)

Dim strOut(10) ' array with 10 positions
Dim xAs Integer = 1 'var to array position
    Do While Not rs.EOF
        With rs
            For Each fld In .Fields
                If fld.Value = "N" Then
                    strOut(x) = strOut(x) & fld.Name & strSeperator
                    x=x+1
                End If

            Next fld
            rs.MoveNext
        End With
    Loop

祝你好运

考虑使用特殊 VBA 函数的 SQL 解决方案,Allen Browne 的 ConcatRelated 在 SQL 查询中调用。将函数复制并保存在 Access 标准模块中。

首先,使用联合查询将宽格式 table 重塑为长格式。

SELECT Surveys.CompanyID, 'Q1' As Question, Surveys.Q1 As Response
FROM Surveys

UNION ALL
SELECT Surveys.CompanyID, 'Q2' As Question, Surveys.Q2 As Response
FROM Surveys

UNION ALL
SELECT Surveys.CompanyID, 'Q3' As Question, Surveys.Q3 As Response
FROM Surveys

UNION ALL
SELECT Surveys.CompanyID, 'Q4' As Question, Surveys.Q4 As Response
FROM Surveys

UNION ALL
SELECT Surveys.CompanyID, 'Q5' As Question, Surveys.Q5 As Response
FROM Surveys

其次,运行条件聚合与ConcatRelated()重塑长回宽

SELECT s.CompanyID, 
       MAX(IIF(s.Question = 'Q1', s.Response)) As Q1,
       MAX(IIF(s.Question = 'Q2', s.Response)) As Q2,
       MAX(IIF(s.Question = 'Q3', s.Response)) As Q3,
       MAX(IIF(s.Question = 'Q4', s.Response)) As Q4,
       MAX(IIF(s.Question = 'Q5', s.Response)) As Q5,
       ConcatRelated("Question", "SurveysUnionQ", 
                     "CompanyID = '" & s.CompanyID & "' AND Response = 'N'") AS NResponses
FROM SurveysLongTableOrUnionQuery s
GROUP BY s.CompanyID


动态解决方案

如果上面有很多题做不出来,通过循环代码构建一个动态联合查询。或者,通过每个 CompanyIDQuestion 迭代创建 table 和 运行 INSERT...SELECT,如下所示:

Public Sub BuildSurveyLongTable()
On Error GoTo Err_Handler
    Dim i As Long, cnt As Long
    Dim db As DAO.Database, tblDef As TableDef

    Set db = CurrentDb
    ' MAKE-TABLE QUERY (RUN ONLY ONCE, COMMENT OUT THEREAFTER)
'    db.Execute "SELECT TOP 1 Surveys.CompanyID, 'Q1' As Question, Surveys.Q1 As Response INTO SurveysLong FROM Surveys"
    db.Execute "DELETE FROM SurveysLong"

    Set tblDef = db.TableDefs("Surveys")

    For i = 2 To tblDef.Fields.Count - 1
        db.Execute "INSERT INTO SurveysLong (CompanyID, Question, Response)" _
                     & " SELECT Surveys.CompanyID, '" & tblDef.Fields(i).name & "' As Question," _
                     & "        Surveys.[" & tblDef.Fields(i).name & "] As Response" _
                     & " FROM Surveys"
    Next i

    MsgBox "Successfully completed!", vbInformation

Exit_Handler:
    Set tblDef = Nothing
    Set db = Nothing
    Exit Sub

Err_Handler:
    MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "RUN-TIME ERROR"
    Resume Exit_Handler
End Sub

同上,下面是条件聚合的动态查询:

Public Sub BuildSurveyQuery()
On Error GoTo Err_Handler
    Dim i As Long
    Dim strSQL As String
    Dim db As DAO.Database, tblDef As TableDef, qdef As QueryDef

    strSQL = "SELECT s.CompanyID, "

    ' ITERATIVELY ADD CONDITIONAL AGGREGATION LINES
    Set db = CurrentDb
    Set tblDef = db.TableDefs("Surveys")

    For i = 2 To tblDef.Fields.Count - 1
        strSQL = strSQL & "MAX(IIF(s.Question = '" & tblDef.Fields(i).name & "', s.Response)) As [" & tblDef.Fields(i).name & "], "
    Next i

    ' REMOVE LAST COMMA
    strSQL = Left(strSQL, Len(strSQL) - 1)

    strSQL = strSQL & " ConcatRelated(""Question"", ""SurveysUnionQ""," _
                    & "              ""CompanyID = '"" & s.CompanyID & ""' AND Response = 'N'"") AS NResponses" _
                    & " FROM SurveysLong s" _
                    & " GROUP BY s.CompanyID"

    ' UPDATE SQL IN QUERY OBJECT AND RELEASE TO SAVE
    Set qdef = db.QueryDefs("SurveysWideConcatQ")
    qdef.SQL = strSQL
    Set qdef = Nothing

    MsgBox "Successfully completed!", vbInformation

Exit_Handler:
    Set tblDef = Nothing
    Set db = Nothing
    Exit Sub

Err_Handler:
    MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "RUN-TIME ERROR"
    Resume Exit_Handler
End Sub

数据透视查询

事实上,条件聚合的替代方法是 Access 独有的 crosstab query,它可以容纳多达 253 个问题(最大列数 255),还包括 ConcatRelated。请注意:NResponses 将出现在问题列的左侧,而不是最右侧的末尾。

TRANSFORM Max(s.Response) AS MaxResponse
SELECT s.CompanyID, 
       ConcatRelated("Question", "SurveysLong",    
                     "CompanyID = '" & s.CompanyID & "' AND Response = 'N'") AS NResponses
FROM SurveysLong s
GROUP BY s.CompanyID
PIVOT s.Question