Сопоставление двух списков данных в Excel VBA и экспорт в новый лист

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

Sub Run_All_Macros()
Application.ScreenUpdating = False
Sheets.Add.Name = "Output"
Call Convert_to_Numbers
Call Highlight_Selected_Contractors
End Sub

'Original Spreadsheet is formatted incorrectly
'Convert PSD Codes to Numbers
Sub Convert_to_Numbers()
Dim xCell As Range
Range("A2:A2500").Select
    For Each xCell In Selection
    xCell.Value = CDec(xCell.Value)
    Next xCell
End Sub


'Highlight Selected Contractors
Sub Highlight_Selected_Contractors()
Dim Full, Selection, Code, SelectedCode As Range
Worksheets("Sheet1").Select
'Set all cells in Column A Sheet 1 to Full
Set Full = Worksheets("Sheet1").Range("A1", Range("A1").End(xlDown))
'Set all cells in Column A Sheet 2 to Selection
Worksheets("Sheet2").Select
Set Selection = Worksheets("Sheet2").Range("A1", Range("A1").End(xlDown))
'If the numbers match highlight the cell
For Each Code In Full
    For Each SelectedCode In Selection
        If Code.Value = SelectedCode.Value Then
       *** Code.Select
        Selection.Copy
        Sheets.Select ("Output")
        ActiveSheet.Paste
    End If
Next SelectedCode
Next Code
End Sub

После выполнения этого кода столбец A в «Выводе» заполняется нулями из A2: A2500. Из-за возни с точками останова я определил, что проблема заключается в том, где я разместил *, но я не уверен, что не так с тем, что там написано.

Спасибо


person Alistair Weir    schedule 24.04.2012    source источник
comment
Вы объявили Selection как вариант. Вы никогда не должны использовать зарезервированные слова (выбор) в качестве переменной. Быстрый вопрос. На каком листе вы пытаетесь запустить Convert_to_Numbers и почему?   -  person Siddharth Rout    schedule 24.04.2012
comment
Convert_to_Numbers запускается на «листе 1», я только что понял, что, поскольку я не указал, что он запускается на новом «выходном» листе, поскольку он становится активным после создания. Только что отредактировав его для запуска на правильном листе, я получаю ошибку «400», возникающую из-за строки, которую я пометил звездочкой в ​​исходном вопросе.   -  person Alistair Weir    schedule 24.04.2012
comment
Ага. :) Также вместо цикла используйте VBA Countif() для проверки существования значений, а затем копируйте их.   -  person Siddharth Rout    schedule 24.04.2012
comment
У меня уже есть готовый код, но я хочу, чтобы вы сначала попробовали его :)   -  person Siddharth Rout    schedule 24.04.2012
comment
Это уже кажется гораздо более эффективным способом, чем использование цикла, но я не совсем там. Получение ошибки несоответствия типов в отношении команды countIf. 'Match and export duplicate values Sub Match_And_Export() Dim Identifier, RawData, Request As Range Set Identifier = Worksheets("Sheet2").Range("A2:A2500") Set RawData = Worksheets("Sheet1").Range("A2:A2500") Set Request = Application.WorksheetFunction.CountIf(RawData, "Identifier") Request.Select Selection.Copy Worksheets("Output").Select Selection.Paste End Sub   -  person Alistair Weir    schedule 24.04.2012
comment
Извините за плохое форматирование, как сделать перенос строки в комментариях?   -  person Alistair Weir    schedule 24.04.2012
comment
Вы никогда не публикуете такой код в комментариях. Вы редактируете свой вопрос, а затем вставляете его туда :) Кстати, ответил на ваш вопрос ниже   -  person Siddharth Rout    schedule 24.04.2012


Ответы (1)


В приведенном выше коде есть несколько ошибок, и у меня также есть несколько предложений и, наконец, код.

ОШИБКИ

1) Sheets.Add.Name = "Output" Эта строка выдаст вам ошибку, если уже существует лист с названием "Вывод". Сначала удалите лист, а затем создайте его. Вам должно быть интересно, что если листа нет, то как я могу его удалить? Для таких сценариев вы можете использовать On Error Resume Next, чего следует избегать в большинстве случаев.

2) При работе с диапазонами всегда указывайте, на какой лист вы ссылаетесь, иначе Excel всегда будет считать, что вы имеете в виду «ActiveSheet». Поскольку вы поняли, что Sub Convert_to_Numbers() принимает во внимание Output лист, тогда как вы хотите, чтобы операция выполнялась на «выходном» листе.

3) Dim Full, Selection, Code, SelectedCode As Range Как упоминалось в моих комментариях ранее, избегайте использования зарезервированных слов Excel в качестве переменных. Кроме того, в отличие от VB.Net, если вы объявляете переменные, как в VBA, то только последняя переменная будет объявлена ​​как Range. Остальные 3 будут объявлены как варианты. VB по умолчанию присваивает переменной тип Variant. Переменная типа Variant может содержать данные любого типа: от строк до целых чисел, длинных целых чисел, дат, валюты и т. д. По умолчанию «варианты» являются «самым медленным» типом переменных. Также следует избегать вариантов, поскольку они несут ответственность за возможные «ошибки несоответствия типов». Дело не в том, что мы никогда не должны использовать варианты. Их следует использовать только в том случае, если вы не уверены, что они могут удерживать при выполнении кода.

4) Избегайте использования таких слов, как .ActiveCell, Selection, Select, Activate и т. д. Они являются основной причиной ошибок. Также они замедляют ваш код.

ПРЕДЛОЖЕНИЯ

1) Вместо того, чтобы каждый раз использовать Sheets("WhatEver"), сохраните его в переменной, а затем используйте эту переменную. Сократит ваш код.

2) Делайте отступы в коде :), так намного легче читать

3) Группируйте задачи вместе. Например, если вам нужно что-то сделать с определенным листом, держите его вместе. Его легче читать и вносить изменения, если это необходимо.

4) Вместо жесткого кодирования значений используйте фактические диапазоны. Range("A2:A2500") — классический пример. Всегда ли у вас будут данные до 2500? А если меньше или больше?

5) End(xlDown) никогда не даст вам последнюю строку, если между ними есть пустая ячейка. Чтобы получить последнюю строку в столбце, скажем A в "Лист1", используйте это

Sheets("Sheet1").Range("A" & Rows.Count).End(xlUp).Row`

6) Вместо зацикливания можно использовать WorksheetFunction CountIf(). Циклов следует избегать, насколько это возможно, поскольку они замедляют ваш код.

7) Используйте соответствующую обработку ошибок.

8) Прокомментируйте свой код. Гораздо проще узнать, что делает конкретный код или раздел.

КОД

Option Explicit

Sub Run_All_Macros()
    Dim ws1I As Worksheet, ws2I As Worksheet, wsO As Worksheet
    Dim ws1LRow As Long, ws2LRow As Long, wsOLr As Long
    Dim xCell As Range, rFull As Range, rSelection As Range
    Dim rCode As Range, rSelectedCode As Range

    On Error GoTo Whoa '<~~ Error Handling

    Application.ScreenUpdating = False

    '~~> Creating the Output Sheet
    Application.DisplayAlerts = False
    On Error Resume Next
    Sheets("Output").Delete
    On Error GoTo 0
    Sheets.Add.Name = "Output"
    Application.DisplayAlerts = True

    '~~> Working with 1st Input Sheet
    Set ws1I = Sheets("Sheet1")
    With ws1I
        '~~> Get Last Row of Col A
        ws1LRow = .Range("A" & Rows.Count).End(xlUp).Row
        '~~> Set the range we want to work with
        Set rFull = .Range("A1:A" & ws1LRow)
        '~~> The following is not required unless you want to just format the sheet
        '~~> This will have no impact on the comparision. If you want you can
        '~~> uncomment it
        'For Each xCell In .Range("A2:A" & ws1LRow)
            'xCell.Value = CDec(xCell.Value)
        'Next xCell
    End With

    '~~> Working with 2nd Input Sheet
    Set ws2I = Sheets("Sheet2") '<~~ Input Sheet 2
    ws2LRow = ws2I.Range("A" & Rows.Count).End(xlUp).Row
    Set rSelection = ws2I.Range("A1:A" & ws2LRow)

    '~~> Working with Output Sheet
    Set wsO = Sheets("Output")
    wsO.Range("A1") = "Common values"
    wsOLr = wsO.Range("A" & Rows.Count).End(xlUp).Row + 1

    '~~> Comparison : If the numbers match copy them to Output Sheet
    For Each rCode In rFull
        If Application.WorksheetFunction.CountIf(rSelection, rCode.Value) > 0 Then
            rCode.Copy wsO.Range("A" & wsOLr)
            wsOLr = wsOLr + 1
        End If
    Next rCode

    MsgBox "Done"

LetsContinue:
    Application.ScreenUpdating = True
    Application.DisplayAlerts = True

    Exit Sub
Whoa:
    MsgBox Err.Description
    Resume LetsContinue
End Sub

Дайте мне знать, если вы все еще получаете какие-либо ошибки :)

ХТН

person Siddharth Rout    schedule 24.04.2012
comment
Отлично, это отлично работает для проблемы, которую я описал, спасибо! Я только что понял, что не описал первоначальную проблему должным образом! Мы сопоставили числа в первом столбце, но мне также нужно скопировать данные в других столбцах для выбранных строк. Я собираюсь потратить немного времени на чтение вашего кода, чтобы все понять и, надеюсь, найти решение вышеуказанной проблемы. Спасибо. - person Alistair Weir; 24.04.2012
comment
Нужно ли определять новый диапазон? Это кажется простой командой, но до сих пор удалось добиться ряда ошибок и заполнить выходной лист до бесконечности моим первым значением :) - person Alistair Weir; 24.04.2012
comment
Это то, что вы пытаетесь? ws1I.Rows(rCode.Row).Copy wsO.Rows(wsOLr) В случае сомнений запишите макрос, чтобы получить базовый код ;) - person Siddharth Rout; 24.04.2012
comment
Ах-ха! Пытался снова жестко закодировать, не осознавая этого, определив необходимые столбцы. Столько полезных советов в этом, большое спасибо! - person Alistair Weir; 24.04.2012
comment
+ 1 Красиво прикрыто. Я уверен, что это поможет и другим людям. - person Pradeep Kumar; 24.04.2012