【问题标题】:VBA Code to match values in multiple columns and then transpose corresponding values to separate columnsVBA代码匹配多列中的值,然后将相应的值转置到单独的列
【发布时间】:2019-01-03 19:54:18
【问题描述】:

我的 VBA 技能充其量只是新手,我不知道如何有效地解决这个问题。

目标:匹配案例 ID # 的 AND 客户端名称(一个案例 ID # 可以有多个客户端),如果它们都匹配,则拉 Q基于问题#(问题列)的响应列的响应

我有 2 个源文件和一个目标文件。我已经设法将所有必要的数据从源文件 1(SF1) 提取到目标文件 (DF)。

我需要将数据从 SF2 提取到 DF。

SF2 数据的结构如下:

Case ID    Client Name   Question #   Response
10095      ABS            0.1          50
10095      ABS            0.2          100
10095      ABS            0.3          0
10095      ZZZ            0.1          0
10095      ZZZ            0.2          40
10095      ZZZ            0.3          99
29999      OVFLW          0.1          100

DF 的结构/看起来如下所示:

CASE ID   Client Name   0.1    0.2    0.3   
10095     ABS           50     100    0
10095     ZZZ           0      40     99
29999     OVFLW         100

我拥有的代码能够获得上述所有内容,但无法说明额外变量是客户端名称,以匹配 到 CASE ID。欢迎任何想法/建议。

提前谢谢你。代码如下:

选项显式

Public Sub GrabKpiData3()

Dim sht As Worksheet, sht2 As Worksheet
Dim i As Long, k As Long
Dim lastrow As Long, lastcol, foundrow As Long, foundcol As Long

Dim macrobook As Workbook
Dim macrosheet As Worksheet

Set macrobook = ThisWorkbook
Set macrosheet = macrobook.Worksheets("Macro")

'source
Set sht = Workbooks("SourceFile2.csv").Worksheets("SF2")

'destination
Set sht2 = Workbooks("MacroFile.xlsm").Worksheets("Data")
lastrow = sht.Cells(sht.Rows.Count, 1).End(xlUp).Row

k = 2

For i = 2 To lastrow
    If sht2.Cells(k, 1).Value = sht.Cells(i, 1).Value Then
        'the below 2 rows grab different date values present within SF2. This would change based on match criteria requiring Case ID + Client name
        sht2.Cells(k, 16).Value = sht.Cells(i, 2).Value
        sht2.Cells(k, 17).Value = sht.Cells(i, 3).Value


        lastcol = sht2.Cells(1, sht2.Columns.Count).End(xlToLeft).Column

        'captures responses for 0.1
        sht2.Cells(k, 18).Value = sht.Cells(i, 6).Value

        i = i + 1

        'captures responses for 0.2
        sht2.Cells(k, 19).Value = sht.Cells(i, 6).Value

        i = i + 1

        'captures responses for 0.3
        sht2.Cells(k, 20).Value = sht.Cells(i, 6).Value

        i = i + 1

        sht2.Cells(k, 21).Value = sht.Cells(i, 6).Value

        i = i + 1

        sht2.Cells(k, 22).Value = sht.Cells(i, 6).Value

        k = k + 1

    Else

On Error Resume Next

    End If
Next i

End Sub

【问题讨论】:

  • 您需要 VBA 吗?这可以通过公式来实现。
  • 不幸的是,VBA 是必要的 :( 还涉及其他数据 + 将来可能扩展到其他事物。
  • 您似乎不了解On Error Resume Next 的工作原理。您可能应该删除它,并且仅在绝对需要时使用它(这比您想象的要少见)。对于k,您还需要一个完整的第二个 for 循环。整个k = k +1 不对。
  • @VBAWARD,您应该选择下面哪个答案最有帮助。这样问题就不会一直悬而未决。

标签: excel vba loops multiple-columns transpose


【解决方案1】:

这是一个正常的 VBA 解决方案,应该可以工作(虽然 SQL 很好,但您可能会遇到一些兼容性/版本问题)...

Set sht = Worksheets("SF2")
Set sht2 = Worksheets("DF")
SrcLastRow = sht.Cells(sht.Rows.Count, 1).End(xlUp).Row
DestLastRow = sht2.Cells(sht2.Rows.Count, 1).End(xlUp).Row
For i = 2 To SrcLastRow
    ' Find the row with a matching Case ID/Client Name
    For k = 2 To DestLastRow
        If sht2.Cells(k, 1).Value = sht.Cells(i, 1).Value And _
           sht2.Cells(k, 2).Value = sht.Cells(i, 2).Value Then _
            Exit For
    Next
    ' Updated - Forgot to add new records...
    If k > DestLastRow Then ' it's a new CaseID/Client Name, so add it
        sht2.Cells(k, 1).Value = sht.Cells(i, 1).Value
        sht2.Cells(k, 2).Value = sht.Cells(i, 2).Value
        DestLastRow = DestLastRow + 1
    End If

    q = 3 ' Starting column for Questions, look for a matching question/header (or blank)
    Do Until sht2.Cells(1, q).Value = sht.Cells(i, 3).Value Or sht2.Cells(1, q).Value = vbNullString
        q = q + 1
    Loop
    ' Write the header for the next question, if it doesn't exist
    If sht2.Cells(1, q).Value = vbNullString Then sht2.Cells(1, q).Value = sht.Cells(i, 3).Value

    ' Write the Response
    sht2.Cells(k, q).Value = sht.Cells(i, 4).Value
Next

更新:经过测试和修复的代码可创建新标头。

【讨论】:

  • 抛开版本问题不谈,如果您将来需要更改如何加入数据的逻辑,那么使用实现的东西要容易得多(并且也可以理解!)用于进行基于集合的比较的 SQL。
  • 对延迟回复表示歉意。我一直在努力解决这个问题,并且大部分时间都在工作。我在尝试修改代码以适应我的情况时遇到的问题是需要添加新记录的部分。由于 SF2 仅根据 DF 中存在的 Case ID 已经 提取数据,因此这部分代码似乎在 DF 底部添加了一系列行,导致数据显示不正确.关于如何解决这个问题的任何想法?
  • @Profex:对于进一步的上下文,理想的是(用更简单的语言)DF Macro 检查 SF2 以找到匹配的 CASE ID 和 CLIENT NAME,如果两者都找到,则返回 RESPONSE适当的问题#(在 DF 中是标题,但在 SF2 中检查是该行的一部分)。我希望这是有道理的?
  • @Profex:我已经修改如下。但是,它在底部创建了一行,我发现它在该字段中的循环数据没有 CASE ID 或 CLIENT NAME。知道我的代码有什么问题吗?编辑:抱歉,我无法将代码粘贴到此框中。让我知道是否有其他方法可以向您展示。
  • @VBAWARD,在使用For i ...Next 循环处理任何其他数据之前,您可能需要添加检查 CaseID/Client Name 是否为空。除此之外,请确保在添加新行时增加DestLastRow。如果没有看到您正在使用的更新代码和数据/文件,很难确定。
【解决方案2】:

您可以使用 SQL 来完成这种数据连接。我已经按照你的数据镜像了我的数据,我称我的工作表 SF2 和 DF 与你的示例相对应。添加对Microsoft Active X Data Object version 2.x 的引用以使其正常工作。

Sub GetJoinedData()
    Dim conn        As ADODB.connection: Set conn = New ADODB.connection
    Dim rs          As ADODB.Recordset: Set rs = New ADODB.Recordset
    Dim outputsheet As Worksheet: Set outputsheet = ThisWorkbook.Sheets("Sheet1")
    Dim i           As Long: i = 1

    conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & _
              ThisWorkbook.FullName & ";Extended Properties='Excel 12.0;HDR=YES';"

    'My data is on two sheets named DF and SF2
    SQL = "Select [DF$].*, [SF2$].[Response] from [DF$] " & _
          "INNER JOIN [SF2$] on [SF2$].[Case ID] = [DF$].[Case ID] " & _
          "and [SF2$].[Client Name] = [DF$].[Client Name]"
    rs.Open SQL, conn, adOpenForwardOnly

    'Add headers
    For Each fld In rs.Fields
        outputsheet.Cells(1, i).Value = fld.Name
        i = i + 1
    Next

    'Dump the data
    outputsheet.Range("A2").CopyFromRecordset rs
End Sub

更新

我想我误解了你的第一个问题。我现在明白的是,您正在获取SF2 中的结果并将(枢轴)转换为DF 中的内容。我已经更新了我的代码来做到这一点。

当添加新问题时,它应该允许多个新问题,并且您在此过程中保留列标题。希望对您有所帮助。

Sub GetJoinedData()
    Dim conn        As ADODB.Connection: Set conn = New ADODB.Connection
    Dim rs          As ADODB.Recordset: Set rs = New ADODB.Recordset
    Dim outputsheet As Worksheet: Set outputsheet = ThisWorkbook.Sheets("DF")
    Dim i           As Long: i = 1

    conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & _
              ThisWorkbook.FullName & ";Extended Properties='Excel 12.0;HDR=YES';"

    'My data is on two sheets named DF and SF2
    Sql = "TRANSFORM Max(response) " & _
          "SELECT [case id], [Client Name] " & _
          "FROM [SF2$] " & _
          "GROUP BY [case id], [Client Name] " & _
          "PIVOT [Question #];"

    rs.Open Sql, conn, adOpenForwardOnly

    'Add headers
    For Each fld In rs.Fields
        outputsheet.Cells(1, i).Value = Replace$(fld.Name, "_", ".") 'Fix a SQL formatting issue where _ exists
        i = i + 1
    Next

    'Dump the data
    outputsheet.Range("A2").CopyFromRecordset rs
End Sub

【讨论】:

  • 如果这两个文件是分开的,我将如何修改?例如:我打开 SourceFile2(SF2),然后打开 DestionationFile(DF) 并在 DF 上运行宏。我是否需要定义从 SourceFile2 表 SF2 读取的路径或任何内容?
  • 要使其按原样工作,所有数据都需要位于同一个工作簿或文件中。是否有理由不合并数据?可能需要将其全部导入 Excel 进行比较。或者,您可以将数据存储在 Access 之类的地方。
  • 经过审查,这不会输出 OP 要求的内容。对于同一个案例 ID/客户名称,它有多个记录(行)。此外,Inner Join 将表限制为两个表中的现有案例 ID/客户名称,并删除其他所有内容。在 SQL 中解决这个问题要困难得多,特别是如果您想动态添加列(即问题 0.4)。这是一个很好的例子,说明了如何使用 SQL 从工作表中读取数据,但最终它不起作用。话虽如此,我也需要修正我的答案。
  • @Profex 你将如何加入 5 个表? 10? 100?这是一个完整的循环。另一种解决方案不太适合额外的范围。
  • @RyanWildry,我只需调用例程并将源和目标工作表传递给它;范围/规模问题得到解决。额外的循环没什么大不了的(尤其是与打开文件所需的时间相比)。是不是最快的,否;有一些改进,例如使用变体数组读取/写入数据,但这会使事情复杂化。我不认为速度是这里的主要问题。 (仅供参考,如果源文件尚未在 Excel 中打开,你的速度会快 10 倍,如果是,我的速度会快 10 倍)
猜你喜欢
  • 2022-01-14
  • 2014-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-11
  • 1970-01-01
  • 1970-01-01
  • 2021-05-05
相关资源
最近更新 更多