Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/excel/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
VBA-Excel从不同位置的不同工作表中获取相同数据_Excel_Vba_Optimization - Fatal编程技术网

VBA-Excel从不同位置的不同工作表中获取相同数据

VBA-Excel从不同位置的不同工作表中获取相同数据,excel,vba,optimization,Excel,Vba,Optimization,我想制作一个宏,它将打开文件夹中的所有excel书籍,读取填写的信息,并将它们存储在一张代表我的数据库的表格中。 我需要知道你的建议,以及最好的方法是什么,以获得快速灵活的结果 为了帮助您理解我的问题,让我们假设我有3个excel模板,包含名字、姓氏和国家,但位置不同,如这些图片 模板1 模板2 模板3 基于此,我希望得到的最终结果是: 我通过这些图片给出的例子非常简单,但这只是为了帮助你理解我想要什么。现在我将详细说明真正的需要。事实上,我有3个模板,但每个模板都包含大约80个数据字段

我想制作一个宏,它将打开文件夹中的所有excel书籍,读取填写的信息,并将它们存储在一张代表我的数据库的表格中。 我需要知道你的建议,以及最好的方法是什么,以获得快速灵活的结果

为了帮助您理解我的问题,让我们假设我有3个excel模板,包含名字、姓氏和国家,但位置不同,如这些图片

模板1

模板2

模板3

基于此,我希望得到的最终结果是:

我通过这些图片给出的例子非常简单,但这只是为了帮助你理解我想要什么。现在我将详细说明真正的需要。事实上,我有3个模板,但每个模板都包含大约80个数据字段,不仅要收集名字、姓氏和国家。我不必只读取3个文件,但我必须读取放置在文件夹中的大约200个文件,每个文件要么是template1,要么是2或3。在未来,我们可能会有一个模板4,这就是为什么我需要一些灵活的

我考虑过命名范围,但模板1、2、3已经存在,我无法从200个用户那里收集200个现有的excels文件,在启动宏之前,为每个文件的80个字段指定一个命名范围。如果将来它们将是模板4,我可以使用命名范围,因此在将文件发送给将填写excel的最终用户之前,我们将命名范围并发送给他,但在模板4之前,我必须修复当前3个现有模板的问题

我还考虑了基于列和行索引读取数据,例如,我检查文件类型,如果我正在读取文件模板1,我从单元格2,3中获取名字,如果是模板2,我从单元格5,6获取信息,如果是模板3,我从单元格9,4获取信息,但问题是,我的代码一点也不灵活

我还说过,我可能会喜欢一张名为reference的表格,其中我根据模板模型定义了每个字段的位置,例如,我说模板1的名字在位置2,3,模板2的名字在位置5,6,模板3的名字在位置9,4。如下图所示,当我循环浏览我的200个文件时,我检查,如果是模板1,我阅读了参考表,我知道第一个名称将位于此位置,与模板2相同,依此类推……此解决方案与前一个解决方案类似,但更灵活,因为如果有什么变化,我们需要更改的只是参考表,但我想知道,如果我必须阅读参考表中的2个单元格才能知道位置,那么每个字段的速度是快还是慢。

我真的迷路了,因为在开始编码之前,我必须选择最好的方式来做我想做的事情,以避免浪费时间。 如果任何专家能告诉我什么是最好的,或者给我比我想的更多的想法,我会非常感激

提前感谢任何帮助者

编辑: @PEH,如果我这样做查找表,你会怎么想?

编辑2: @佩赫,这就是上次评论中所建议的

循环浏览文件的基本思想:

将查找数据更改为以下内容:

然后阅读单元格1、6以获得您的模型

Dim Model As String
Model = Worksheets("MyTemplate").Cells(1, 6).Value
使用在查找表中查找字段

Dim FieldRow As Long
FieldRow = Application.WorksheetFunction.Match(Model & "-First name", Worksheets("LookupTable").Range("A:A"), 0)
使用

fRow = Worksheets("LookupTable").Cells(FieldRow, 2)
fColumn = Worksheets("LookupTable").Cells(FieldRow, 3)
获取要在模板中查找该字段的行和列

如果您将字段查找内容放入一个方便的函数中,代码将更易于维护。例如,将以下内容放入模块中:

Option Explicit

Public LookupCache As Variant
Public LookupResults As Variant

Public Function ReadField(Ws As Worksheet, FieldName As String) As Variant
    'Here we cache the lookup table. It reads the sheet LookupTable into an 
    'array if the array does not exist yet. If the function runs a second time,
    'the array exists already and is used directly (saves time).
    'Lookup in arrays is much faster than in cells.
    'Caching makes this function about 2 times faster than without.
    If IsEmpty(LookupCache) Or IsEmpty(LookupResults) Then
        With ThisWorkbook.Worksheets("LookupTable")
            Dim LastLookupRow As Long
            LastLookupRow = .Cells(.Rows.Count, "A").End(xlUp).Row
            LookupCache = .Range("A2", "A" & LastLookupRow).Value
            LookupResults = .Range("B2", "C" & LastLookupRow).Value
        End With
    End If

    Dim ModelName As String
    ModelName = Ws.Cells(1, 6).Value

    Dim LookupRow As Long
    On Error Resume Next
    LookupRow = Application.WorksheetFunction.Match(ModelName & "-" & FieldName, LookupCache, 0)
    On Error GoTo 0

    If LookupRow = 0 Then
        'field not found
        ReadField = CVErr(xlErrNA)
        Exit Function
    End If

    Dim fRow As Long, fColumn As Long
    fRow = LookupResults(LookupRow, 1)
    fColumn = LookupResults(LookupRow, 2)

    ReadField = Ws.Cells(fRow, fColumn).Value
End Function
所以你可以读一个像

Debug.Print ReadField(MyLoopWorkbook.Worksheets("MyTemplate"), "First name")
'MyLoopWorkbook should be the current workbook in your files loop
根据评论编辑

如果我们在新的model4中添加了一个新的字段公司,用户必须转到工作表lookuptable并在第11行添加model4公司,行和列,但也必须在代码中添加ReadFieldMyLoopWorkbook.WorksheetsMyNewTemplate,Company,对吗?这就是为什么我不明白为什么我只能依靠那些不编写代码的人来添加它?请你澄清一下,因为你所说的很重要

如果将ReadField部件设置为动态,则不需要在此处编写代码。例如,如果您希望得到这样一个表:

您只需在第4列中添加一个新的标题,名为like字段eg Company。并编写一个循环,循环该标题行的列以收集所有字段

Sub ReadAllFields()
    Dim wsData As Worksheet
    Set wsData = Worksheets("CollectedData")

    Dim FreeRow As Long 'find next free row in table
    FreeRow = wsData.Cells(wsData.Rows.Count, "A").End(xlUp).Row + 1

    Dim Fields() As Variant 'read headers into array
    Fields = wsData.Range("A1", wsData.Cells(1, wsData.Columns.Count).End(xlToLeft)).Value

    Dim iCol As Long
    For iCol = 1 To UBound(Fields, 2) 'loop through header columns
        wsData.Cells(FreeRow, iCol).Value = ReadField(MyLoopWorkbook.Worksheets("MyNewTemplate"), Fields(1, iCol)) 
        'reads fields dynamically depending on which headers exist in data sheet
    Next iCol
End Sub

佩赫,你提供了答案吗?我看到你写了一些东西,但看不到你写了什么。看看这个。您可以使用它来搜索名字并使用找到的单元格,而无需事先知道其位置。那么你只需要学会如何去做。这应该给你一个开始的地方。@JustGreat不,只是更正了你的标签。但我正要写与泰格拉瓦塔尔相同的评论你的问题太宽泛了,无法提供答案,因为你什么也没做。给出方法
在上面的评论中,我尝试了一下。多亏了这两个功能…我已经知道如何在一个文件夹中循环,这不是问题。。。至于Range.Find,您的意思是我检查每个数据的标题以获得值?这比告诉我需要位置1,2的数据快吗?我也在想一些事情。range.find将提供标题的位置,因此我应该进行偏移以获得正确的值?如果是,这意味着如果有一天,我们在标题和值之间添加一列、一行或一个小单元格,我应该检查整个代码,对吗?模型1等的名称是否总是在单元格1、6中?然后,您可以读取此单元格以确定它是哪个模型以及其他数据的预期位置。这比使用“查找”要快。@太好了您忘了将PEH的答案标记为选中。将该表从您的格式转换为我的格式将是同样的混乱,转换和不一致的数据没有什么好处,只有缺点。万一你再问我,我还是会告诉你不要那样做在Rng vs row/col中,范围可能会稍微快一点,因为您只需要处理1个变量而不是2个变量。范围可能对用户更方便。无论如何,这两种方法都可以工作,并且不会有太大的区别。@JustGreat几乎应该是Model-Field来处理我的代码。Excel公式从左到右的计算速度更快,因此最好的列顺序是模板、字段,然后是自动填充。@JustGreat范围和行/列之间的性能差异最小,因为您只能将其读入数组缓存一次,因此您可以忽略这1/10秒,您可能会在这里获胜。使用更适合您和您的用户的工具。•Excel中的计算顺序是从左/上到右/下,因此,如果在A1=B1+C1中计算,B1和C1也是公式,则Excel从A1开始,然后计算B1,从而再次触发A1中的更改,然后计算C1,再次触发A1中的更改。所以它必须多次计算A1。所以最好是计算C1=A1+B1。@JustGreat它就是这样设计的;