【问题标题】:Combine 2 Excel tables into one appending the data?将 2 个 Excel 表合并为一个附加数据?
【发布时间】:2012-04-12 23:34:31
【问题描述】:

我在 MS Excel 2007 工作簿的 2 个单独工作表上有 2 个表格,如下所示:

===========================
no.   f_name     l_name  
===========================
13   Little     Timmy
1   John       Doe
17   Baby       Jessica
---------------------------


===========================
no.   f_name     l_name  
===========================
1   john       Tim
16   kyle       joe
14   Baby       katy
22   qbcd       wsde
---------------------------

两者都有相同的列,但它们可以有不同的数据。

我想将两个表的数据垂直组合,即一个表与第三张单独的表中的所有数据。 如果可能,我想添加另一列,其中包含该行所在的工作表名称。

===================================
SheetName   no.   f_name     l_name  
===================================
Sheet1      13   Little     Timmy
Sheet1      1   John       Doe
Sheet1      17   Baby       Jessica
Sheet2      1   john       Tim
Sheet2      16   kyle       joe
Sheet2      14   Baby       katy
Sheet2      22   qbcd       wsde
-----------------------------------

不使用宏能做到吗?

【问题讨论】:

    标签: excel append


    【解决方案1】:

    您可以激活 Office 剪贴板(功能区主页选项卡上剪贴板部分右下角的箭头)。复制两个范围,然后使用 全部粘贴 命令,如下所示。

    您仍然需要先在额外的列中填写工作表名称,但可以通过双击填充手柄来完成。

    更新

    要使用公式获得相同的结果,请尝试填写工作表名称:

    =IF(ROW()<=COUNTA(Sheet1!A:A),"Sheet1",IF(ROW()<COUNTA(Sheet1:Sheet2!A:A),"Sheet2",""))
    

    然后在这个公式中填写表格中的值:

    =IF(ROW()<=COUNTA(Sheet1!A:A),Sheet1!A2,IF(ROW()<COUNTA(Sheet1:Sheet2!A:A),INDEX(Sheet2!A:A,ROW()-COUNTA(Sheet1!A:A)+1),""))
    

    【讨论】:

    • 谢谢!!是的..这是一个选项,但问题是我还希望最终表在我对任何输入表进行更改时自动更新......我不知道我是否可以在这里使用任何功能。我一直在寻找它一段时间,但找不到任何东西。也许宏是这里唯一的选择:(
    • 您可以使用 paste-special Paste Link 命令将组合表链接到源表。如果列表中的项目数不变,这将随着原始表中的更改而自动更新
    • 我改进了@lori_m 的答案,使其更能容忍在源表上插入一行:=IF(ROW()&lt;(COUNTA(Sheet1!A:A)+1),INDEX(Sheet1!A:A,ROW()),IF(ROW()&lt;COUNTA(Sheet1:Sheet2!A:A),INDEX(Sheet2!A:A,ROW()-COUNTA(Sheet1!A:A)+1),"")
    【解决方案2】:

    lori_m 做出了非常好的贡献,我通过使用 Microsoft Excel 表格和结构化引用来构建它。

    首先在输出表中创建一个名为 RowID 的列,其中包含表中的行号,然后使用它来填充数据值。

    =IF( INDIRECT("Table3[RowId]")<=ROWS(Table1)
        ,INDEX(Table1[column1],INDIRECT("Table3[RowId]"))
        ,INDEX(Table2[Column1],INDIRECT("Table3[RowId]")-ROWS(Table1)))
    

    有一个detailed explanation of how this works on my blog,因为它太长了,无法包含在此处。

    【讨论】:

      【解决方案3】:

      此答案涉及 Excel 解释的Structured Tables。虽然这些方法可以很容易地转录为原始数据矩阵而无需分配表结构,但该解决方案的公式和 VBA 编码将针对真正的结构化表。

      序言

      第三个表可以使用一些本机工作表公式维护两个表的组合数据,但是在从相关表中添加或删除行时保持第三个表的大小正确需要手动调整大小操作或一些跟踪这些数据的 VBA更改并符合第三张表以适应。我在此答案的末尾包含了添加源表的工作表名称以及一些表维护 VBA 代码的选项。

      如果您想要的只是一个操作示例工作簿而没有所有解释,请跳到此答案的末尾以获取指向用于创建此过程的工作簿的链接。

      示例数据表

          

      我使用 OP 的示例数据在工作表 Sheet1 和 Sheet2 上分别构建了两个名为(默认情况下)Table1 和 Table2 的表。我有意将它们从每个工作表的 A1 单元格中不同程度地偏移,以证明结构化表格能够将公式中的自身或另一个结构化表格作为单独的实体来处理,而不管其在父工作表上的位置如何。第三个表将以类似的方式构建。这些偏移仅用于演示目的;它们不是必需的。

      第一步:构建第三张表

      为第三个表格构建标题并选择未来的标题行及其下方的至少一行作为插入 ► 表格 ► 表格命令的基础。

              

      Sheet3 工作表上新的第三个空表应类似于以下内容。

          

      第 2 步:填充第三个表

      首先填充第三个表的DataBodyRange 中的第一个单元格。在本例中,这将是 Sheet3!C6。在 C6 中键入或粘贴以下公式,记住它基于默认表名称。如果您更改了表名,请进行相应调整。

      =IFERROR(INDEX(Table1, ROW([@[no.]])-ROW(Table3[#Headers]),COLUMN(A:A)), INDEX(Table2, ROW([@[no.]])-ROW(Table3[#Headers])-ROWS(Table1),COLUMN(A:A)))
      

      INDEX function 首先从 Table1 中检索每个可用行。实际的行号是通过ROW function 引用结构化表的已定义部分以及一些数学运算得出的。当 Table1 的行用完时,检索将传递给 IFERROR function 引用 Table2 的第二个 INDEX 函数,并使用 ROW 和 ROWS functions 使用更多的数学来检索其顺序行。 COLUMN function 用作COLUMN(A:A),它将检索引用表的第一列,而不管它在工作表上的什么位置。当公式正确填写时,这将进入第二列、第三列等。

      说到填权,把公式填到E6。你应该有一些近似于以下的东西。

          

      步骤 2.5:[可选] 添加源表的父工作表名称

      在右下角抓住 Table3 的大小调整手柄(由下图示例中的橙色箭头指示)并将其向右拖动一列以向表格添加新列。将标题标签重命名为比默认值更合适的名称。我使用 Sheet 作为列标签。

              

      虽然您无法直接检索源表的工作表名称,但 CELL function 可以检索已保存工作簿中任何单元格的完全限定路径、文件名和工作表¹作为其可选info_types之一.

      将以下公式放入刚刚创建的新列第一行的Table3的空单元格中。

      =TRIM(RIGHT(SUBSTITUTE(CELL("filename", IF((ROW([@[no.]])-ROW(Table3[#Headers]))>ROWS(Table1), Table2, Table1)), CHAR(93), REPT(CHAR(32), 999)), 255))
      

      比较填充 Table3

      如果您不打算在从两个源表中的任何一个中添加或删除行时使用一些 VBA 来维护 Table3 的尺寸,那么只需抓住 Table3 的调整大小手柄并向下拖动,直到您收集了所有数据从两个表。有关预期结果的示例图像,请参阅此答案的底部。

      如果您打算添加一些 VBA,则跳过 Table3 的全部填充并继续下一步。

      第三步:添加一些VBA来维护第三张表

      由工作表数据更改触发的流程的完全自动化最好由工作表的Worksheet_Change 事件宏处理。由于涉及三个表,每个表都在自己的工作表中,Workbook_SheetChange event macro 是处理来自多个工作表的更改事件的更好方法。

      使用 Alt+F11 打开 VBE。打开后,在左上角寻找 Project Explorer。如果它不可见,则点击 Ctrl+R 将其打开。找到 ThisWorkbook 并右键单击,然后选择查看代码(或直接双击 ThisWorkbook)。

              

      将以下内容粘贴到标题为 Book1 - ThisWorkbook (Code) 的新窗格中。

      Option Explicit
      
      Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
          Select Case Sh.Name
              Case Sheet1.Name
                  If Not Intersect(Target, Sheet1.ListObjects("Table1").Range.Offset(1, 0)) Is Nothing Then
                      On Error GoTo bm_Safe_Exit
                      Application.EnableEvents = False
                      Call update_Table3
                  End If
              Case Sheet2.Name
                  If Not Intersect(Target, Sheet2.ListObjects("Table2").Range.Offset(1, 0)) Is Nothing Then
                      On Error GoTo bm_Safe_Exit
                      Application.EnableEvents = False
                      Call update_Table3
                  End If
          End Select
      
      bm_Safe_Exit:
          Application.EnableEvents = True
      End Sub
      
      Private Sub update_Table3()
          Dim iTBL3rws As Long, rng As Range, rngOLDBDY As Range
          iTBL3rws = Sheet1.ListObjects("Table1").DataBodyRange.Rows.Count
          iTBL3rws = iTBL3rws + Sheet2.ListObjects("Table2").DataBodyRange.Rows.Count
          iTBL3rws = iTBL3rws + Sheet3.ListObjects("Table3").DataBodyRange.Cells(1, 1).Row - _
                                Sheet3.ListObjects("Table3").Range.Cells(1, 1).Row
          With Sheet3.ListObjects("Table3")
              Set rngOLDBDY = .DataBodyRange
              .Resize .Range.Cells(1, 1).Resize(iTBL3rws, .DataBodyRange.Columns.Count)
              If rngOLDBDY.Rows.Count > .DataBodyRange.Rows.Count Then
                  For Each rng In rngOLDBDY
                      If Intersect(rng, .DataBodyRange) Is Nothing Then
                          rng.Clear
                      End If
                  Next rng
              End If
          End With
      End Sub
      

      这两个例程广泛使用了工作表.CodeName property。工作表的 CodeNameSheet1、Sheet2、Sheet3 等,并且在重命名工作表时不会更改。事实上,即使是更高级的用户也很少更改它们。它们已被使用,以便您可以重命名工作表而无需修改代码。但是,他们现在应该指向正确的工作表。如果您的表格和工作表与给定的不同,请修改代码。您可以在上图中显示 VBE 的项目资源管理器的工作表 .Name property 旁边的括号中看到各个工作表的代号。

      点击 Alt+Q 返回您的工作表。剩下的就是通过选择 Table1Table2 中的任何单元格并假装通过点击 F2 来修改它来完成填充 Table3 然后 Enter↵。您的结果应类似于以下内容。

          

      如果您一直跟随到这里,那么您应该有一个非常全面的集合表,它主动组合了来自两个源“子”表的数据。如果您还添加了 VBA,则几乎不需要维护第三个集合表。

      重命名表格

      如果您选择重命名三个表中的任何一个或全部,工作表公式将立即自动反映更改。如果您选择包含 Workbook_SheetChange 和随附的帮助程序子过程,则必须返回 ThisWorkbook 代码表并使用 Find & Replace 进行适当的更改。

      示例工作簿

      我已经从我的公共 DropBox 中提供了完全可操作的示例工作簿。

      Table_Collection_w_Sheetname.xlsb


      ¹ CELL function 只能检索已保存工作簿的工作表名称。如果工作簿尚未保存,则它没有文件名,当要求输入文件名时,CELL 函数将返回一个空字符串。

      【讨论】:

      • 这是一篇很棒的文章并且很有效。我将如何添加第三个要附加的表?
      • 写得很好。常见问题的简单解决方案。
      • 很好的答案!很好的综合性文章以及解决方案的工作原理。
      • @Jeeped :您的回答很棒,而且非常详细。示例文件似乎不再可用。能再分享一下吗?
      • @AntoineDriard,我复制了工作簿并将副本粘贴到谷歌驱动器上。我已经更新了上面回复中的链接。
      【解决方案4】:

      对 Jeeped 的代码稍作修改。

      如果您碰巧使用了类似的方法,但有多个表(例如超过 10 个),那么尝试手动添加每个表的每个名称会相当麻烦。如果您更改表的名称,这也是一个问题,因为这些名称在 VBA 中是硬连线的。为避免额外的工作,请考虑以下事项:

      所以,假设如下:

      • 在每个工作表上都有一个或多个表格,但它们具有相似的结构。
      • 工作表上只有表格 - 没有 ListObjects 集合的其他成员。
      • 每次我们在工作表上编辑表时,都会触发主表(表 3)中的更新。

      那么上面示例中的 Workbook_SheetChange Sub 可能如下所示:

           Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
           Dim tbl As ListObject
           For Each tbl In ActiveSheet.ListObjects
              If Not Intersect(Target, tbl.Range.Offset(1, 0)) Is Nothing Then
                  On Error GoTo bm_Safe_Exit
                  Application.EnableEvents = False
                  Call update_Table
              End If
              Next tbl
              bm_Safe_Exit:
              Application.EnableEvents = True
           End Sub
      

      编辑。第二个例程将如下所示:

         Private Sub update_Table()
          Dim iTBL3rws As Long, rng As Range, rngOLDBDY As Range
          Dim tbl As ListObject
          Dim sht As Worksheet
          iTBL3rws = 0
      
          ' consider all tables, excluding master table
          For Each sht In ThisWorkbook.Worksheets
              For Each tbl In sht.ListObjects
                  If tbl.Name <> "Table3" Then
                      iTBL3rws = iTBL3rws + tbl.DataBodyRange.Rows.Count
                  End If
              Next tbl
          Next sht
      
          iTBL3rws = iTBL3rws + Sheet3.ListObjects("Table3").DataBodyRange.Cells(1, 1).Row - Sheet3.ListObjects("Table3").Range.Cells(1, 1).Row
              With Sheet3.ListObjects("Table3")
      
                  Set rngOLDBDY = .DataBodyRange
      
                  .Resize .Range.Cells(1, 1).Resize(iTBL3rws, .DataBodyRange.Columns.Count)
      
                  If rngOLDBDY.Rows.Count > .DataBodyRange.Rows.Count Then
                      For Each rng In rngOLDBDY
                          If Intersect(rng, .DataBodyRange) Is Nothing Then
                              rng.Clear
                          End If
                      Next rng
                  End If
              End With
      
      End Sub
      

      此例程与以前的例程不同,它消除了预编程的情况。当活动工作表上注册了更改时,此工作表中将要更改的任何表都将触发 update_Table 过程。

      【讨论】:

        【解决方案5】:

        我正在使用此代码/公式。很好地满足我的需要我唯一想知道的是如何制作更好的单元格公式,以便我可以使用 3 个以上的表格作为参考。目前我只是在 iferror 中嵌套了一堆 iferror 语句

        =IFERROR(INDEX(Table1, ROW([@Date])-ROW(Table3[#Headers]),COLUMN(A:A)),IFERROR( INDEX(Table2, ROW([@Date])-ROW(Table3[#Headers])-ROWS(Table1),COLUMN(A:A)), IFERROR(INDEX(Table4, ROW([@Date])-ROW(Table3[#Headers])-ROWS(Table2)-ROWS(Table1),COLUMN(A:A)),INDEX(Table5, ROW([@Date])-ROW(Table3[#Headers])-ROWS(Table2)-ROWS(Table1)-ROWS(Table4),COLUMN(A:A)))))

        我也在用

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-08-04
          • 2016-11-15
          • 2012-04-13
          • 1970-01-01
          • 2019-05-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多