Пропустить несколько строк кода, если нет совпадения - PullRequest
0 голосов
/ 15 марта 2020

Pi c заголовков Consultants.xlsm Pi c заголовков Customers.xlsx

У меня есть два файла Excel. Одна содержит данные, обозначенные как «электронная таблица с информацией о клиенте», а вторая (с макросом) - это место, где пользователь может вводить данные для сопоставления с консультантом в «consultants.xlsm». Электронная таблица «Информация о клиенте» содержит клиентов / заказы, которые должны соответствовать консультанту, который будет открыт одновременно с выполнением макроса. Когда макрос запускается, имя вводится в электронную таблицу «Информация о клиенте», извлекаемую из электронной таблицы консультанта, начиная со столбца «a», затем переходя к столбцу «b» и так далее.

Мне нужна помощь в том, что… если нет соответствия между информацией о клиенте и консультантом, то перейдите к следующему набору инструкций. У меня «DIM rngConsultant» до 12 консультантов, но я показываю только 3 ниже. Я уверен, что это уродливый код, но это то, что я получил по большей части:

Sub FilterbyConsultant()

     Dim rngConsultant As Range
     With Workbooks("Consultants.xlsm").Sheets("Consultants")
     Set rngConsultant = .Range("A3", .Range("A" & Columns.Count).End(xlUp))
     End With
     Range("A1:A25000").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=rngConsultant, Unique:=False

    Range("B1").Select
    ActiveCell.FormulaR1C1 = Workbooks("Consultants.xlsm").Sheets("Consultants").Range("A1")
    Range("B1").Select
    Selection.Copy
    Range("B12").Select
    Selection.End(xlDown).Select
    ActiveSheet.Paste
    Range(Selection, Selection.End(xlUp)).Select
    ActiveSheet.Paste
    Selection.Font.Bold = False
    Range("B1").Select
    ActiveCell.FormulaR1C1 = "Consultant"
    Range("A1").Select
    ActiveSheet.ShowAllData
    Selection.AutoFilter

     Dim rngConsultant2 As Range
     With Workbooks("Consultants.xlsm").Sheets("Consultants")
     Set rngConsultant2 = .Range("B3", .Range("B" & Columns.Count).End(xlUp))
     End With
     Range("A1:A25000").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=rngConsultant2, Unique:=False

    Range("B1").Select
    ActiveCell.FormulaR1C1 = Workbooks("Consultants.xlsm").Sheets("Consultants").Range("B1")
    Range("B1").Select
    Selection.Copy
    Range("B25").Select
    Selection.End(xlDown).Select
    ActiveSheet.Paste
    Range(Selection, Selection.End(xlUp)).Select
    ActiveSheet.Paste
    Range("B1").Select
    Application.CutCopyMode = False
    ActiveCell.FormulaR1C1 = "Consultant"
    Range("A1").Select
    ActiveSheet.ShowAllData
    Selection.AutoFilter


     Dim rngConsultant3 As Range
     With Workbooks("Consultants.xlsm").Sheets("Consultants")
     Set rngConsultant = .Range("C3", .Range("C" & Columns.Count).End(xlUp))
     End With
     Range("A1:A25000").AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=rngConsultant3, Unique:=False

    Range("B1").Select
    ActiveCell.FormulaR1C1 = Workbooks("Consultants.xlsm").Sheets("Consultants").Range("C1")
    Range("B1").Select
    Selection.Copy
    Range("B37").Select
    Selection.End(xlDown).Select
    ActiveSheet.Paste
    Range(Selection, Selection.End(xlUp)).Select
    ActiveSheet.Paste
    Range("B1").Select
    Application.CutCopyMode = False
    ActiveCell.FormulaR1C1 = "Consultant"
    Range("A1").Select

1 Ответ

0 голосов
/ 16 марта 2020

Я предполагаю, что вы создали большую часть своего кода с помощью Macro Recorder. Устройство записи макросов не знает вашей цели, поэтому записывает каждое действие по мере его выполнения. Результат синтаксически правильный, но очень плохой VBA. Например, Select - медленная команда, которую любой учебник скажет вам не использовать. У Macro Recorder нет выбора. Вы добавили заявление With, которое не может использовать Macro Recorder.

Оскорбив ваше использование Macro Recorder, я признаю, что использовал его сам. Я не использую Import CSV file, Find and Sort достаточно часто, чтобы запомнить синтаксис или все параметры. Я обычно включаю Макро-рекордер, вручную выполняю одну из этих сложных команд, выключаю Макро-рекордер, а затем настраиваю записанный код в соответствии с рекомендуемыми стандартами.

Я создал две рабочие книги, которые соответствуют вашим описаниям: «Консультанты .xlsm "и" CustomerInfo.xlsx ". Для рабочего листа «Консультанты» я выбрал полдюжины имен из своего местного телефонного справочника и поместил их вдоль строки 1. Затем, начиная со строки 4, я разместил от одного до пяти названий компаний под каждым консультантом. Я создал лист «Заказы» в CustomerInfo.xlsx и скопировал выборку названий компаний в столбец A, начиная со строки 4.

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

Создав рабочие книги и рабочие таблицы, я выполнил поиск одной из компаний в рабочей таблице «Консультанты» и использовал Macro Recorder для захвата синтаксиса. В результате получилось:

Sub Macro1()
    Cells.Select
    Selection.Find(What:="Spitfire Insurance", After:=ActiveCell, LookIn:= _
        xlValues, LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:= _
        xlNext, MatchCase:=False, SearchFormat:=False).Activate
End Sub

Затем я убрал этот код для своего макроса. Изучите разницу между приведенным выше кодом и окончательным кодом в моем макросе. Попробуйте понять, почему я сделал каждое изменение. При необходимости возвращайтесь с вопросами, но чем больше вы сможете работать самостоятельно, тем быстрее вы будете развивать свои навыки VBA.

Мой код ниже содержит два макроса: AssignConsultantToOrder() и FindLastRowCol().

В Excel VBA есть несколько способов поиска последней строки и столбца листа, но ни один из них не работает в любой ситуации. Раньше я решал, какую технику использовать для каждого нового макроса. Затем я решил написать макрос, который будет работать в любой известной мне ситуации, и добавить его в свою библиотеку. Я использую PERSONAL.XLSB в качестве библиотеки. У меня есть модули с именами «LibExcel», «LibOutlook» и «LibOffice». В эти модули я поместил макросы, разработанные для Excel, Outlook или любого пакета Microsoft Office. FindLastRowCol() находится в «LibExcel». Он использует каждую технику для поиска последней строки и столбца, а затем выбирает лучшие ответы. Не волнуйтесь об этой рутине на данный момент. Если у вас есть свободное время, я думаю, вы должны его изучить. Вы также можете подумать, что стоит создать PERSONAL.XLSB и скопировать в него этот макрос, чтобы он был доступен любому написанному макросу Excel.

Я считаю, что в AssignConsultantToOrder() достаточно комментариев, чтобы вы могли понять, Это. Однако при необходимости вернитесь с вопросами.

Option Explicit

  ' I avoid using numeric literal for row and column numbers.
  ' If a column, for example, is moved, working through your code looking for
  ' every reference to that column is a nightmare. If you use constant, one
  ' change and your code is adjusted.

  Const RowConsultantsName As Long = 1
  Const RowConsultantsDataFirst As Long = 4

  Const ColOrdersCustomer As Long = 1
  Const ColOrdersConsultant As Long = 2
  Const RowOrdersDataFirst As Long = 4
Sub AssignConsultantToOrder()

  Dim ColOrdersLast As Long
  Dim ConsultantName As String
  Dim CustomerName As String
  Dim RngConsultant As Range
  Dim RowOrdersCrnt As Long
  Dim RowOrdersLast As Long
  Dim WbkConsultants As Workbook
  Dim WbkOrders As Workbook
  Dim WshtConsultants As Worksheet
  Dim WshtOrders As Worksheet

  ' I have created references to both workbooks.  It should not matter
  ' which workbook or worksheet is active when you start the macro
  Set WbkConsultants = Workbooks("Consultants.xlsm")
  Set WbkOrders = Workbooks("CustomerInfo.xlsx")

  Set WshtConsultants = WbkConsultants.Worksheets("Consultants")
  Set WshtOrders = WbkOrders.Worksheets("Orders")

  Call FindLastRowCol(WshtOrders, RowOrdersLast, ColOrdersLast)

  ' When I am developing a macro, I tend to write it in stages and use
  ' Debug.Print to prove that each stage is working before starting the
  ' next stage.
  'Debug.Print "Orders " & RowOrdersLast & " " & ColOrdersLast

  For RowOrdersCrnt = RowOrdersDataFirst To RowOrdersLast
    CustomerName = WshtOrders.Cells(RowOrdersCrnt, ColOrdersCustomer).Value
    'Debug.Print CustomerName
    With WshtConsultants
      Set RngConsultant = _
                .Cells.Find(What:=CustomerName, After:=.Cells(RowConsultantsDataFirst - 1, 1), _
                            LookIn:=xlValues, LookAt:=xlWhole, SearchOrder:=xlByColumns, _
                            SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
      If RngConsultant Is Nothing Then
        ' Customer not found so do not know consultant
        ConsultantName = ""
      Else
        ' Extract consultant name
        ConsultantName = .Cells(RowConsultantsName, RngConsultant.Column).Value
        'Debug.Print ConsultantName
      End If
    End With
    ' Copy consultant name to orders worksheet
    WshtOrders.Cells(RowOrdersCrnt, ColOrdersConsultant).Value = ConsultantName
  Next

End Sub
Public Sub FindLastRowCol(ByRef Wsht As Worksheet, ByRef RowLast As Long, _
                          ByRef ColLast As Long)

  ' Sets RowLast and ColLast to the last row and column with a value
  ' in worksheet Wsht

  ' The motivation for coding this routine was the discovery that Find by
  ' previous row found a cell formatted as Merge and Center but Find by
  ' previous column did not.
  ' I had known the Find would miss merged cells but this was new to me.

  '   Dec16  Coded
  ' 31Dec16  Corrected handling of UserRange
  ' 15Feb17  SpecialCells was giving a higher row number than Find for
  '          no reason I could determine.  Added code to check for a
  '          value on rows and columns above those returned by Find
  ' 25Jun17  Found column with value about that found by Find

  Dim ColCrnt As Long
  Dim ColLastFind As Long
  Dim ColLastOther As Long
  Dim ColLastTemp As Long
  Dim ColLeft As Long
  Dim ColRight As Long
  Dim Rng As Range
  Dim RowIncludesMerged As Boolean
  Dim RowBot As Long
  Dim RowCrnt As Long
  Dim RowLastFind As Long
  Dim RowLastOther As Long
  Dim RowLastTemp As Long
  Dim RowTop As Long

  With Wsht

    Set Rng = .Cells.Find("*", .Range("A1"), xlFormulas, , xlByRows, xlPrevious)
    If Rng Is Nothing Then
      RowLastFind = 0
      ColLastFind = 0
    Else
      RowLastFind = Rng.Row
      ColLastFind = Rng.Column
    End If

    Set Rng = .Cells.Find("*", .Range("A1"), xlValues, , xlByColumns, xlPrevious)
    If Rng Is Nothing Then
    Else
      If RowLastFind < Rng.Row Then
        RowLastFind = Rng.Row
      End If
      If ColLastFind < Rng.Column Then
        ColLastFind = Rng.Column
      End If
    End If

    Set Rng = .Range("A1").SpecialCells(xlCellTypeLastCell)
    If Rng Is Nothing Then
      RowLastOther = 0
      ColLastOther = 0
    Else
      RowLastOther = Rng.Row
      ColLastOther = Rng.Column
    End If

    Set Rng = .UsedRange
    If Rng Is Nothing Then
    Else
      If RowLastOther < Rng.Row + Rng.Rows.Count - 1 Then
        RowLastOther = Rng.Row + Rng.Rows.Count - 1
      End If
      If ColLastOther < Rng.Column + Rng.Columns.Count - 1 Then
        ColLastOther = Rng.Column + Rng.Columns.Count - 1
      End If
    End If

    If RowLastFind < RowLastOther Then
      ' Higher row found by SpecialCells or UserRange
      Do While RowLastOther > RowLastFind
        ColLastTemp = .Cells(RowLastOther, .Columns.Count).End(xlToLeft).Column
        If ColLastTemp > 1 Or .Cells(RowLastOther, 1).Value <> "" Then
          Debug.Assert False
          ' Is this possible
          ' Row after RowLastFind has value
          RowLastFind = RowLastOther
          Exit Do
        End If
        RowLastOther = RowLastOther - 1
      Loop
    ElseIf RowLastFind > RowLastOther Then
      Debug.Assert False
      ' Is this possible
    End If
    RowLast = RowLastFind

    If ColLastFind < ColLastOther Then
      ' Higher column found by SpecialCells or UserRange
      Do While ColLastOther > ColLastFind
        RowLastTemp = .Cells(.Rows.Count, ColLastOther).End(xlUp).Row
        If RowLastTemp > 1 Or .Cells(1, ColLastOther).Value <> "" Then
          'Debug.Assert False
          ' Column after ColLastFind has value
          ' Possible causes:
          '   * Find does not recognise merged cells
          '   ' Find does not examine hidden cells
          ColLastFind = ColLastOther
          Exit Do
        End If
        ColLastOther = ColLastOther - 1
      Loop
    ElseIf ColLastFind > ColLastOther Then
      Debug.Assert False
      ' Is this possible
    End If
    ColLast = ColLastFind

  End With

End Sub
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...