将2个Excel表合并为一个附加数据?

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

=========================== 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 ----------------------------------- 

可以不使用macros来完成吗?

这个答案处理由Excel解释的结构化表格 。 虽然方法可以很容易地转换为原始数据matrix没有分配表结构,这个解决scheme的公式和VBA编码将针对真正的结构化表。

前言

第三个表可以使用一些本地工作表公式保持两个表的组合数据,但是将第三个表的大小保持正确,因为行被添加到从属表或从附属表中删除将需要手动resize操作或某些VBA跟踪这些更改和符合第三张表适合。 我已经添加了选项来添加源表的工作表名称以及一些表维护VBA代码在这个答案的结尾。

如果您只想要一个没有任何解释的操作示例工作簿,请跳至此答案的末尾,以获取用于创build此过程的工作簿的链接。

示例数据表

表收集示例数据

我已经使用OP的示例数据来构build两个表,分别在工作表Sheet1和Sheet2上分别命名为(默认情况下)Table1和Table2。 为了演示一个结构化表格能够在公式中将自己或另一个结构化表格作为单独的实体,而不考虑它在父级工作表上的位置,我故意将其偏移到每个工作表的A1单元格中。 第三张桌子将以类似的方式build造。 这些偏移量仅用于演示目的; 他们不是必需的。

第1步:build立第三个表

构build第三个表格的标题并select将来的标题行,并在其下面至less一行,以插入►表格►表格命令为基础。

结合表新表

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

收集表格数据建立新表

第二步:填写第三个表

首先填充第三个表的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函数首先从Table1中检索每个可用的行。 实际的行号是通过ROW函数引用结构化表的定义部分和一些小math推导出来的。 当Table1用完行时,通过IFERROR函数将检索传递给引用Table2的第二个INDEX函数,并使用更多的math运算,使用ROW和ROWS函数检索其顺序行。 COLUMN函数用作COLUMN(A:A) ,它将检索被引用表的第一列,而不pipe它在工作表上的何处。 由于公式是正确的,这将进展到第二,第三等。

说到灌装权,请填写E6的配方。 你应该有一些近似于下面的东西。

汇总表格数据,第三个表格

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

抓住Table3的右下angular的大小手柄(在下面的示例图像中用橙色箭头表示),并向右拖动一列以在表格中添加新的列。 将标题标签重命名为比默认更适合的东西。 我已经使用Sheet作为列标签。

整理表格数据 - 源工作表名称

尽pipe无法直接检索源表的工作表名称,但CELL函数可以检索已保存工作簿¹中任何单元格的完全限定path,文件名和工作表,作为其可选的info_types之一

将下面的公式放到刚创build的新列的第一行的Table3的空单元格中。

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

Complate填充Table3

如果您不打算用一些VBA来完成这个小项目,以便在从两个源表中添加或删除行时维护Table3的维度,那么只需抓取Table3的resize手柄并向下拖动,直到您已经累积了来自两个表的所有数据。 请参阅此答案的底部以获取预期结果的示例图像。

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

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

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

Alt + F11打开VBE。 一旦你打开它,find左上angular的Project Explorer 。 如果它不可见,请点击Ctrl + R将其打开。 findThisWorkbook并右键单击然后select查看代码(或者只是双击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属性 。 工作表的CodeNameSheet1,Sheet2,Sheet3等 ,并且在工作表重命名时不会更改。 事实上,即使是更高级的用户也很less改变它们。 他们已经被使用,所以你可以重命名你的工作表,而不必修改代码。 但是,他们现在应该指向正确的工作表。 如果您的表格和工作表与给定的不一致,请修改代码。 您可以在上图中的工作表.Name属性旁边的括号内看到单个工作表代码名,其中显示了VBE的Project Explorer。

点击Alt + Q返回到您的工作表。 剩下的工作就是通过select表1 2中的任何单元格并假装通过点击F2然后按Enter键进行修改来完成填充表格3 。 你的结果应该类似于以下内容。

将两个表自动合并为一个表

如果你一路追踪到这里,那么你应该有一个非常全面的收集表,积极地结合来自两个来源“子”表的数据。 如果您添加了VBA,那么维护第三个收集表几乎是不存在的。

重命名表

如果您select重命名三个表中的任何一个或全部,工作表公式将立即自动反映这些更改。 如果您已select包含Workbook_SheetChange和伴随辅助程序,则必须返回到ThisWorkbook代码表,并使用“查找和replace”进行适当的更改。

示例工作簿

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

Table_Collection_w_Sheetname.xlsb


¹CELLfunction只能检索保存的工作簿的工作表名称。 如果一个工作簿没有被保存,那么它没有文件名,CELLfunction在询问文件名时将返回一个空string。

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

您仍然需要首先填写表格名称,但可以通过双击填充柄来完成。

在这里输入图像说明

更新

为了得到与公式相同的结果,请填写下面的表格名称:

 =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),"")) 

lori_m通过使用Microsoft Excel表和结构化引用为我提供了非常好的贡献。

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

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

有一个详细的解释,这是如何工作在我的博客,因为这是太长,包括在这里。

对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过程。

我使用这个代码/公式。 适合我的需求只是我想知道的是如何使一个更好的细胞公式,所以我可以使用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)))))