2013-02-28 1 views
0

Честное слово, я искал и искал, и хотя может существовать существующий ответ на аналогичный вопрос, я не могу найти Это. Это говорит:Мне нужно извлечь данные из определенных столбцов в одной книге и добавить их к существующим данным в другую книгу

  • У меня есть данные в двух разных книгах, и мне нужно, чтобы скопировать данные из выбранного числа несмежных столбцов в файле генерироваться каждую неделю, и добавить его к определенным столбцам существующих данных в мастер файл

  • Я получаю новый файл каждую неделю по имени results.data.xls и содержит 5 столбцов данных с переменным количеством записей - несколько недель, это может быть два рядом, другим это может быть 200+

  • Я хочу, чтобы иметь возможность копировать данные, которые отображаются в столбце «B» ('PRODUCT_FORMAT_CAPACITY'), столбец " D»('CUSTOMER') и столбец„E“(BILLTO_CUSTOMER_NUM) в results.data.xls и добавить его к существующим данным в столбцах аналогичного названия в master.data.xls

  • Запись макроса не получают меня очень далеко поскольку я явно не могу понять, что нужно добавить данные - я видел команды VBA, которые позволят это, но я не могу понять, как изменить простую запись, чтобы заставить ее делать то, что мне нужно

ответ

0

Устройство записи макросов идеально подходит для обнаружения синтаксиса сложных команд с одной инструкцией. Но если вы делаете A тогда B, то C, рекордер записывает их как три полностью независимые команды, даже если они являются фазами одной команды.

Чтобы проверить код ниже:

  • Я создал книгу 'master.data.xls' и внутри него, рабочий лист 'Комбинированный'. Вы не упоминаете имена своих рабочих листов, поэтому я сделал свой собственный. Я возглавлял три столбца «PRODUCT_FORMAT_CAPACITY», «CUSTOMER» и «BILLTO_CUSTOMER_NUM», но не столбцы B, D и E. Я помещал некоторые случайные данные в эти столбцы.
  • Я создал книгу «results.data.xls» и, внутри нее, рабочий лист «Неделя». Я возглавлял столбцы B, D и E 'PRODUCT_FORMAT_CAPACITY', 'CUSTOMER' и 'BILLTO_CUSTOMER_NUM'. Я помещал некоторые случайные данные в эти столбцы.
  • Я создал макрос в отдельной книге. Я предпочитаю держать свои макросы в отдельных книгах, чтобы пользователи (1) не беспокоились о них, и (2) не могут их изменить.

Вы не говорите, если вы новичок в программировании или новичок в VBA. Я предположил, что вы новичок в программировании. Большая часть приведенного ниже кода касается проверки ваших предположений и изящного изящества, если все не так, как ожидалось.

Существует несколько альтернативных методов поиска нижней строки или самой правой колонки, ни одна из которых не работает в каждой ситуации. Я выбрал один из тех методов для кода ниже. См. Мой ответ для демонстрации некоторых альтернатив: https://stackoverflow.com/a/18220846/973283.

Надеюсь, это поможет.

' "Option Explicit" stops a mispelt name becoming a declaration. Without 
' "Option Explicit" the following will define a new variable Conut. Such 
' errors can be very difficult to find: 
' Dim Count As Long 
' Conut = Count + 1 
Option Explicit 

    ' Use constants for values that will not change during a run of the macro 
    ' particularly if you have to use them several times or if purpose of the 
    ' value is not obvious. "Cells(Row,2)" is a lot harder to understand than 
    ' "Cells(Row,ColResultProduct)". I have used WBkMasterName several times. 
    ' If the workbook is renamed, changing the constant declaration fixes the 
    ' problem. 
    Const ColResultProduct As Long = 2 
    Const ColBillToName As String = "BILLTO_CUSTOMER_NUM" 
    Const ColCustomerName As String = "CUSTOMER" 
    Const ColProductName As String = "PRODUCT_FORMAT_CAPACITY" 
    Const WBkMasterName As String = "master.data.xls" 
    Const WBkResultName As String = "results.data.xls" 
    Const WShtMasterName As String = "Combined" 
    Const WShtResultName As String = "Week" 

    ' My naming convention is ABC where A is the type (Col for column, WBk for 
    ' workbook, etc), B identifies the particular A (for example, for Col, B 
    ' identifies the worksheet) and C identifies which AB if there is more than 
    ' one (for ColMaster I have ColMasterProduct, ColMasterBillTo, ColMasterCrnt 
    ' (Crnt = Current), etc. You may not like my naming convention. Fine, pick 
    ' your own or, better still, agree one with colleagues. Conventions mean 
    ' you can look at the program you wrote twelve months ago or your colleague 
    ' wrote and understand the variables. 

    ' My comments tell you my objective or my reason for selecting method A and 
    ' not B. They do not explain VBA syntax. For example, once you know the 
    ' Workbooks.Open statement exists, it is easy to find an explanation of its 
    ' syntax within the VBA help or via an internet search, 

Sub Demo() 

    Dim ColMasterBillTo As Long 
    Dim ColMasterCrnt As Long 
    Dim ColMasterCustomer As Long 
    Dim ColMasterLast As Long 
    Dim ColMasterProduct As Long 
    Dim ColResultBillTo As Long 
    Dim ColResultCustomer As String 
    Dim CountMasterColFoundCrnt As Long 
    Dim CountMasterColFoundTotal As Long 
    Dim InxWBkCrnt As Long 
    Dim PathCrnt As String 
    Dim RngResult As Range 
    Dim RowMasterNext As Long 
    Dim RowResultLast As Long 
    Dim TempStg As String 
    Dim WBkMaster As Workbook 
    Dim WBkResult As Workbook 
    Dim WShtMaster As Worksheet 
    Dim WShtResult As Worksheet 

    ' ThisWorkbook identifies the workbook containing the macro. 
    ' I will assume the data workbooks are in the same folder as 
    ' the macro workbook. 
    PathCrnt = ThisWorkbook.Path 

    ' You do not want to run this macro when someone has the data workbooks open 
    ' so check for them being within the collection of open workbooks. 
    For InxWBkCrnt = 1 To Workbooks.Count 
    If Workbooks(InxWBkCrnt).Name = WBkMasterName Then 
     Call MsgBox("Please close workbook '" & WBkMasterName & _ 
            "' before running this macro.", vbOKOnly) 
     Exit Sub 
    End If 
    If Workbooks(InxWBkCrnt).Name = WBkResultName Then 
     Call MsgBox("Please close workbook '" & WBkResultName & _ 
            "' before running this macro.", vbOKOnly) 
     Exit Sub 
    End If 
    Next 

    ' The next blocks of code check that the workbooks exist and contain the 
    ' expected worksheets with the expected columns. You may think that this 
    ' code is unnecessary and I hope you are right. However, if something is 
    ' wrong, do you want your macro to fail unexpectedly with a yellow statement 
    ' and an error message a programmer may find difficult to understand or 
    ' corrupt data because columns have moved or do you want the macro to close 
    ' tidily with an error message that the user understands? 

    ' "On Error Resume Next" Statement "On Error GoTo 0" switches off normal 
    ' error processing for "Statement". You can then check if "Statement" 
    ' has had the expected result. Some statements set Err.Number and 
    ' Err.Description if they fail but Workbooks.Open does not. 

    ' You can use Dir$() to check for the file existing but (1) I think the 
    ' approach below is marginally easier and (2) Dir$() checks for existence 
    ' not openability. 

    ' Try to open data workbooks. Report failure to the user. 
    On Error Resume Next 
    Workbooks.Open PathCrnt & "\" & WBkMasterName 
    On Error GoTo 0 

    If ActiveWorkbook.Name = ThisWorkbook.Name Then 
    Call MsgBox("I was unable to open workbook " & _ 
               WBkMasterName & "'.", vbOKOnly) 
    Exit Sub 
    End If 
    Set WBkMaster = ActiveWorkbook 

    On Error Resume Next 
    Workbooks.Open PathCrnt & "\" & WBkResultName 
    On Error GoTo 0 

    If ActiveWorkbook.Name = WBkMaster.Name Then 
    Call MsgBox("I was unable to open workbook '" & _ 
               WBkResultName & "'.", vbOKOnly) 
     ' Tidy up by closing open workbook and releasing resource 
     WBkMaster.Close SaveChanges:=False 
     Set WBkMaster = Nothing 
    Exit Sub 
    End If 
    Set WBkResult = ActiveWorkbook 

    ' Try to reference worksheets 
    With WBkMaster 
    On Error Resume Next 
    Set WShtMaster = .Worksheets(WShtMasterName) 
    On Error GoTo 0 
    If WShtMaster Is Nothing Then 
     Call MsgBox("Workbook '" & WBkMasterName & "' does not contain " & _ 
        "worksheet '" & WShtMasterName & "'.", vbOKOnly) 
     WBkMaster.Close SaveChanges:=False 
     WBkResult.Close SaveChanges:=False 
     Set WBkMaster = Nothing 
     Set WBkResult = Nothing 
     Exit Sub 
    End If 
    End With 

    With WBkResult 
    On Error Resume Next 
    Set WShtResult = .Worksheets(WShtResultName) 
    On Error GoTo 0 
    If WShtResult Is Nothing Then 
     Call MsgBox("Workbook '" & WBkResultName & "' does not contain " & _ 
        "worksheet '" & WShtResultName & "'.", vbOKOnly) 
     WBkMaster.Close SaveChanges:=False 
     WBkResult.Close SaveChanges:=False 
     Set WBkMaster = Nothing 
     Set WBkResult = Nothing 
     Exit Sub 
    End If 
    End With 

    With WShtResult 

    ' I have defined 'ColResultProduct' with a constant. That will be the best 
    ' approach unless you know to expect a particular type of change. 

    ' I use "Debug.Assert Boolean-expression" extensively during development. 
    ' In particular, I place "Debug.Assert False" above every alternative path 
    ' through my code. When I hit a "Debug.Assert False" during testing, I 
    ' comment it out. If any remain at the end of testing I know that either 
    ' my testing was not as thorough as it should be or I have allowed for 
    ' a situation that does not exist. Either way, the code needs review. 
    ' Leaving a "Debug.Assert" statement in code you release to users would be 
    ' very unprofessional. 
    Debug.Assert .Cells(1, ColResultProduct).Value = ColProductName 

    ' In a Cells object, the column can be a number or a letter. Use whichever 
    ' you prefer. I do not like statements like this buried in the code. This 
    ' should be a constant statement at the top of the module. 
    ColResultCustomer = "D" 

    If .Cells(1, ColResultCustomer).Value <> ColCustomerName Then 
     ' Note the use of property Address as an easy way of converting a VBA 
     ' style address to a user style address. Note also the use of Replace to 
     ' remove the dollar signs from "$D$1" to give "D1" 
     Call MsgBox("Cell " & Replace(.Cells(1, ColResultCustomer).Address, "$", "") _ 
      & " of worksheet '" & WShtResultName & "' of workbook '" & _ 
      WBkResultName & "' is not " & ColCustomerName & ".", vbOKOnly) 
     WBkMaster.Close SaveChanges:=False 
     WBkResult.Close SaveChanges:=False 
     Set WBkMaster = Nothing 
     Set WBkResult = Nothing 
     Exit Sub 
    End If 

    ColResultBillTo = 5   ' Again, this should be a constant 
    If .Cells(1, ColResultBillTo).Value <> ColBillToName Then 
     Call MsgBox("Cell " & Replace(.Cells(1, ColResultBillTo).Address, "$", "") _ 
      & " of worksheet '" & WShtResultName & "' of workbook '" & _ 
      WBkResultName & "' is not " & ColBillToName & ".", vbOKOnly) 
     WBkMaster.Close SaveChanges:=False 
     WBkResult.Close SaveChanges:=False 
     Set WBkMaster = Nothing 
     Set WBkResult = Nothing 
     Exit Sub 
    End If 
    End With 

    With WShtMaster 

    ' Do not consider anything like this code unless columns are moved 
    ' regularly. It is so easy to waste time preparing for situations that will 
    ' never occur. You could amend three constants many times more quickly than 
    ' you can get code like this debugged. I have code like this because I 
    ' have situations in which columns moving is likely to occur and I do 
    ' not want my diverse users coming back to me when it does. 

    ColMasterLast = .Cells(1, Columns.Count).End(xlToLeft).Column 
    CountMasterColFoundTotal = 0 
    ColMasterBillTo = 0 
    ColMasterCustomer = 0 
    ColMasterProduct = 0 
    ' Look for the three headers and record their columns. Record 
    ' number of headers found. 
    For ColMasterCrnt = 1 To ColMasterLast 
     If .Cells(1, ColMasterCrnt).Value = ColBillToName Then 
     CountMasterColFoundTotal = CountMasterColFoundTotal + 1 
     ColMasterBillTo = ColMasterCrnt 
     ElseIf .Cells(1, ColMasterCrnt).Value = ColCustomerName Then 
     CountMasterColFoundTotal = CountMasterColFoundTotal + 1 
     ColMasterCustomer = ColMasterCrnt 
     ElseIf .Cells(1, ColMasterCrnt).Value = ColProductName Then 
     CountMasterColFoundTotal = CountMasterColFoundTotal + 1 
     ColMasterProduct = ColMasterCrnt 
     End If 
    Next 
    If CountMasterColFoundTotal <> 3 Then 
     ' One or more column has not been found 
     CountMasterColFoundCrnt = 3 
     TempStg = "I cannot find column headings" 
     If ColMasterProduct = 0 Then 
     'Debug.Assert False 
     TempStg = TempStg & " " & ColProductName 
     CountMasterColFoundCrnt = CountMasterColFoundCrnt - 1 
     If CountMasterColFoundCrnt - 1 >= CountMasterColFoundTotal Then 
      'Debug.Assert False 
      TempStg = TempStg & " or" 
     'Else 
      'Debug.Assert False 
     End If 
     'Else 
     'Debug.Assert False 
     End If 
     If ColMasterCustomer = 0 Then 
     'Debug.Assert False 
     TempStg = TempStg & " " & ColCustomerName 
     CountMasterColFoundCrnt = CountMasterColFoundCrnt - 1 
     If CountMasterColFoundCrnt - 1 >= CountMasterColFoundTotal Then 
      'Debug.Assert False 
      TempStg = TempStg & " or" 
     'Else 
      Debug.Assert False 
     End If 
     'Else 
     'Debug.Assert False 
     End If 
     If ColMasterBillTo = 0 Then 
     'Debug.Assert False 
     TempStg = TempStg & " " & ColBillToName 
     'Else 
     'Debug.Assert False 
     End If 
     TempStg = TempStg & " in row 1 of worksheet '" & _ 
       WShtMasterName & "' of workbook '" & WBkMasterName & "'." 
     Call MsgBox(TempStg, vbOKOnly) 
     WBkMaster.Close SaveChanges:=False 
     WBkResult.Close SaveChanges:=False 
     Set WBkMaster = Nothing 
     Set WBkResult = Nothing 
     Exit Sub 
    End If 
    End With 

    ' If get here then both workbooks are as required. 

    ' Find last row of results worksheet and next row of master worksheet 
    ' Copy product column from results to master 
    With WShtResult 
    RowResultLast = .UsedRange.Row + .UsedRange.Rows.Count - 1 
    Set RngResult = .Range(.Cells(2, ColResultProduct), _ 
          .Cells(RowResultLast, ColResultProduct)) 
    End With 
    With WShtMaster 
    RowMasterNext = .UsedRange.Row + .UsedRange.Rows.Count 
    RngResult.Copy Destination:=.Cells(RowMasterNext, ColMasterProduct) 
    End With 

    ' Copy customer column from results to master 
    With WShtResult 
    Set RngResult = .Range(.Cells(2, ColResultCustomer), _ 
          .Cells(RowResultLast, ColResultCustomer)) 
    End With 
    With WShtMaster 
    RngResult.Copy Destination:=.Cells(RowMasterNext, ColMasterCustomer) 
    End With 

    ' Copy bill to column from results to master 
    With WShtResult 
    Set RngResult = .Range(.Cells(2, ColResultBillTo), _ 
          .Cells(RowResultLast, ColResultBillTo)) 
    End With 
    With WShtMaster 
    RngResult.Copy Destination:=.Cells(RowMasterNext, ColMasterBillTo) 
    End With 

    WBkMaster.Close SaveChanges:=True 
    WBkResult.Close SaveChanges:=False 

End Sub 
Смежные вопросы