Do NOT loop through all cells!! There is a lot of overhead in communications between worksheets and VBA, for both reading and writing. Looping through all cells will be agonizingly slow. I’m talking hours.
Instead, load an entire sheet at once into a Variant array. In Excel 2003, this takes about 2 seconds (and 250 MB of RAM). Then you can loop through it in no time at all.
In Excel 2007 and later, sheets are about 1000 times larger (1048576 rows × 16384 columns = 17 billion cells, compared to 65536 rows × 256 columns = 17 million in Excel 2003). You will run into an «Out of memory» error if you try to load the whole sheet into a Variant; on my machine I can only load 32 million cells at once. So you have to limit yourself to the range you know has actual data in it, or load the sheet bit by bit, e.g. 30 columns at a time.
Option Explicit
Sub test()
Dim varSheetA As Variant
Dim varSheetB As Variant
Dim strRangeToCheck As String
Dim iRow As Long
Dim iCol As Long
strRangeToCheck = "A1:IV65536"
' If you know the data will only be in a smaller range, reduce the size of the ranges above.
Debug.Print Now
varSheetA = Worksheets("Sheet1").Range(strRangeToCheck)
varSheetB = Worksheets("Sheet2").Range(strRangeToCheck) ' or whatever your other sheet is.
Debug.Print Now
For iRow = LBound(varSheetA, 1) To UBound(varSheetA, 1)
For iCol = LBound(varSheetA, 2) To UBound(varSheetA, 2)
If varSheetA(iRow, iCol) = varSheetB(iRow, iCol) Then
' Cells are identical.
' Do nothing.
Else
' Cells are different.
' Code goes here for whatever it is you want to do.
End If
Next iCol
Next iRow
End Sub
To compare to a sheet in a different workbook, open that workbook and get the sheet as follows:
Set wbkA = Workbooks.Open(filename:="C:MyBook.xls")
Set varSheetA = wbkA.Worksheets("Sheet1") ' or whatever sheet you need
Перейти к основному содержанию
Статья даёт ответы на следующие вопросы:
- Как сравнить две таблицы в Excel с помощью макросов VBA?
- Как обращаться к ячейкам таблицы Excel с помощью VBA?
- Как осуществлять перебор ячеек таблицы в цикле с помощью VBA?
В предыдущей статье Сравнение таблиц в Excel мы рассмотрели подход к сравнению сложных таблиц с использованием формул и без программирования.
В данной статье рассмотрим способ сравнения таблиц Excel с помощью VBA макросов на примере тех же исходных данных.
Проиллюстрируем задачу картинкой из первой статьи.
Для начала напишем алгоритм наших действий по сравнению таблиц.
- Определим диапазоны данных первой и второй таблицы, то есть найдем последние значимые строки и сохраним их номера в переменных (последняя строка таблицы 1 — last_i и последняя строка таблицы 2 — last_j).
- Начнем проходить по каждой строке таблицы 2 (внешний цикл), данные из которой нужно перенести в таблицу 1. С первой строки данных (в примере это строка 3) до последней строки таблицы 2.
- Для каждой строки таблицы 2 определим идентификатор строки, путем формирования строки, содержащей полный адрес квартиры (значения из нескольких колонок, разделенные дефисами).
- Начнем проходить по каждой строке таблицы 1 (внутренний цикл) с первой строки данных (в примере это строка 3) до последней строки таблицы 1, определяя при этом идентификатор строки.
- Сравним значения идентификаторов строк таблицы 1 и таблицы 2.
- Если идентификаторы равны, перепишем ФИО покупателя из ячейки таблицы 2 в соответствующую ячейку таблицы 1; прервем внутренний цикл по таблице 1 и перейдем к следующей строке таблицы 2 (переход к п.2).
Теперь остается реализовать алгоритм в виде программного кода макроса.
Для этого откроем вкладку Вид ленты функций Excel. Щелкнем на нижнюю часть со стрелкой кнопки Макросы. В открывшемся подменю выберем Запись макроса. В результате начнется запись нового макроса. Поскольку код мы будем формировать вручную, то еще раз зайдем в подменю макросов и выберем Остановить запись. Далее еще раз войдем в подменю макросов и выберем Макросы.
В появившемся диалоге выделим наш макрос и нажмем Изменить.
На экране откроется окно редактора макросов Visual Basic for Applications. В области кода (правая верхняя область) отображается код только что созданного пустого макроса.
В процедуру Макрос1 (между объявлениями начала и конца процедуры: Sub и End Sub) необходимо вставить код, решающий поставленную задачу.
Образец кода представлен ниже.
Sub Макрос1() ' ' Макрос1 сравнение двух таблиц с использованием макроса VBA ' ' ссылка на первый лист книги Dim sheet1 As Worksheet Set sheet1 = ActiveWorkbook.Sheets(1) ' ссылка на второй лист книги Dim sheet2 As Worksheet Set sheet2 = ActiveWorkbook.Sheets(2) ' строка для хранения идентификатора строки первой таблицы Dim str1 As String ' строка для хранения идентификатора строки второй таблицы Dim str2 As String ' позиция курсора (номер строки) в первой таблице Dim i As Integer i = 3 Dim last_i As Integer last_i = 3 ' позиция курсора (номер строки) во второй таблице Dim j As Integer j = 3 Dim last_j As Integer last_j = 3 ' определяем последнюю значимую строку первой таблицы (последняя строка, в первой колонке которой есть значение) For Each Cell In sheet1.Range("A:A") If Cell.Row > 2 Then If Cell.Value > "" Then last_i = Cell.Row Else Exit For End If End If Next Cell ' определяем последнюю значимую строку второй таблицы (последняя строка, в первой колонке которой есть значение) For Each Cell In sheet2.Range("A:A") If Cell.Row > 2 Then If Cell.Value > "" Then last_j = Cell.Row Else Exit For End If End If Next Cell ' пробегаем по строкам второй таблицы (внешний цикл) For j = 3 To last_j ' определяем идентификатор текущей строки str2 = sheet2.Cells(j, 1).Value & "-" & sheet2.Cells(j, 2).Value & "-" & sheet2.Cells(j, 3).Value & "-" & sheet2.Cells(j, 4).Value ' пробегаем по строкам первой таблицы (внутренний цикл) For i = 3 To last_i ' определяем идентификатор текущей строки str1 = sheet1.Cells(i, 1).Value & "-" & sheet1.Cells(i, 2).Value & "-" & sheet1.Cells(i, 3).Value & "-" & sheet1.Cells(i, 4).Value ' сравниваем идентификаторы строк первой и второй таблицы If str2 = str1 Then ' если совпадение найдено, то записываем покупателя из второй таблицы в первую в строку с соответствующей ему квартирой sheet1.Cells(i, 5).Value = sheet2.Cells(j, 5).Value ' прекращаем внутренний цикл, переходим к следующей итерации внешнего цикла ' (к следующей записи второй таблицы) Exit For End If Next i Next j End Sub
Результат решения задачи:
Другие интересные статьи
- Как сравнить две таблицы в Excel с использованием формул?
- Горячие клавиши Excel
Тэги:
- Статьи
- Excel
- сравнение таблиц
- VBA
- макросы
kilmynda 0 / 0 / 0 Регистрация: 25.07.2017 Сообщений: 1 |
||||
1 |
||||
25.07.2017, 10:35. Показов 8064. Ответов 2 Метки excel, vba (Все метки)
Добрый день, форумчане! До этого не имел дело с VBA. Начинаю только изучать. Начальство подкинуло такую задачку: Нашел только это:
0 |
Sub QWERT() Dim Q As Object Dim W As Object Dim Qb As Workbook Dim Wb As Workbook Dim Sq Dim Sw Dim Mq() Dim Mw() Dim R1, R2 Dim S1, S2, S3 'открываем книги Set Qb = Workbooks.Open(Filename:="C:Книга2.xls") Set Wb = Workbooks.Open(Filename:="C:Книга3.xls") 'определяем количество строк Sq = Qb.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row Sw = Wb.Sheets(1).Cells(Rows.Count, 1).End(xlUp).Row 'считываем в массив Mq = Qb.Application.Range(Cells(1, 1), Cells(Sq, 1)).Value Mw = Wb.Application.Range(Cells(1, 1), Cells(Sw, 1)) 'загоняем в словари Set Q = CreateObject("Scripting.Dictionary") Set W = CreateObject("Scripting.Dictionary") For R1 = 1 To Sq If Not Q.Exists(Mq(R1, 1)) Then Q.Add Mq(R1, 1), 1 Next R1 For R1 = 1 To Sw If Not W.Exists(Mw(R1, 1)) Then W.Add Mw(R1, 1), 1 Next R1 For R1 = 1 To Sq If W.Exists(Mq(R1, 1)) Then 'если есть S1 = S1 + 1 Лист2.Cells(S1, 1) = Mq(R1, 1) Else 'в противном случае S2 = S2 + 1 Лист3.Cells(S2, 1) = Mq(R1, 1) End If Next R1 S1 = 0 S2 = 0 For R1 = 1 To Sw If Q.Exists(Mw(R1, 1)) Then 'если есть S1 = S1 + 1 Лист4.Cells(S1, 1) = Mw(R1, 1) Else 'в противном случае S2 = S2 + 1 Лист5.Cells(S2, 1) = Mw(R1, 1) End If Next R1 End Sub
Содержание
- Сравнение таблиц в Excel с помощью макросов VBA
- Vba excel сравнение файлов
- Vba excel сравнение файлов
- Vba excel сравнение файлов
- VBA Excel. Сравнение прайс-листов
- Данные для сравнения прайс-листов
- Сравнение прайс-листов в VBA Excel
Сравнение таблиц в Excel с помощью макросов VBA
Оставлен Adm вс, 09/11/2014 — 21:30
Статья даёт ответы на следующие вопросы:
- Как сравнить две таблицы в Excel с помощью макросов VBA?
- Как обращаться к ячейкам таблицы Excel с помощью VBA?
- Как осуществлять перебор ячеек таблицы в цикле с помощью VBA?
В предыдущей статье Сравнение таблиц в Excel мы рассмотрели подход к сравнению сложных таблиц с использованием формул и без программирования.
В данной статье рассмотрим способ сравнения таблиц Excel с помощью VBA макросов на примере тех же исходных данных.
Проиллюстрируем задачу картинкой из первой статьи.
Для начала напишем алгоритм наших действий по сравнению таблиц.
- Определим диапазоны данных первой и второй таблицы, то есть найдем последние значимые строки и сохраним их номера в переменных (последняя строка таблицы 1 — last_i и последняя строка таблицы 2 — last_j).
- Начнем проходить по каждой строке таблицы 2 (внешний цикл), данные из которой нужно перенести в таблицу 1. С первой строки данных (в примере это строка 3) до последней строки таблицы 2.
- Для каждой строки таблицы 2 определим идентификатор строки, путем формирования строки, содержащей полный адрес квартиры (значения из нескольких колонок, разделенные дефисами).
- Начнем проходить по каждой строке таблицы 1 (внутренний цикл) с первой строки данных (в примере это строка 3) до последней строки таблицы 1, определяя при этом идентификатор строки.
- Сравним значения идентификаторов строк таблицы 1 и таблицы 2.
- Если идентификаторы равны, перепишем ФИО покупателя из ячейки таблицы 2 в соответствующую ячейку таблицы 1; прервем внутренний цикл по таблице 1 и перейдем к следующей строке таблицы 2 (переход к п.2).
Теперь остается реализовать алгоритм в виде программного кода макроса.
Для этого откроем вкладку Вид ленты функций Excel. Щелкнем на нижнюю часть со стрелкой кнопки Макросы. В открывшемся подменю выберем Запись макроса. В результате начнется запись нового макроса. Поскольку код мы будем формировать вручную, то еще раз зайдем в подменю макросов и выберем Остановить запись. Далее еще раз войдем в подменю макросов и выберем Макросы.
В появившемся диалоге выделим наш макрос и нажмем Изменить.
На экране откроется окно редактора макросов Visual Basic for Applications. В области кода (правая верхняя область) отображается код только что созданного пустого макроса.
В процедуру Макрос1 (между объявлениями начала и конца процедуры: Sub и End Sub) необходимо вставить код, решающий поставленную задачу. Образец кода представлен ниже.
Результат решения задачи:
Источник
Vba excel сравнение файлов
Здравствуйте, уважаемые гуру!
Есть такая интересная задача — сравнить два файла Excel.
Нужно, чтобы макрос спрашивал с какой строки сравнивать, так как до шапки может идти ненужный текст. И несовпадающие (соответствующие. ) ячейки закрасить в какой-нить цвет!
Всем буду очень признателен за ответ!
——— примечание модератора — вдруг кому пригодится —————
Надстройка LOOKUP предназначена для сравнения и подстановки значений в таблицах Excel.
Если вам надо сравнить 2 таблицы (по одному столбцу, или по нескольким),
и для совпадающих строк скопировать значения выбранных столбцов из одной таблицы в другую,
надстройка «Lookup» поможет сделать это нажатием одной кнопки.
В настройках программы можно задать:
- где искать сравниваемые файлы (использовать уже открытый файл, загружать файл по заданному пути, или же выводить диалоговое окно выбора файла)
- с каких листов брать данные (варианты: активный лист, лист с заданным номером или названием)
- какие столбцы сравнивать (можно задать несколько столбцов)
- значения каких столбцов надо копировать в найденные строки (также можно указать несколько столбцов)
Скачать надстройку для сравнения таблиц Excel и копирования данных из одинаковых строк
Вложения
сми.rar (7.4 Кб, 316 просмотров) |
Это не сложно!
Но если Вы заметили, в Ваших двух файлах таблицы начинаются со 2-ой строки и с 4-ой. Это надо учитывать?
Искать несоответствие надо только в области таблиц?
Ладно, неважно.
Вот пример:
Вложения
сми.zip (22.8 Кб, 274 просмотров) |
to MAXX Спасибо большое за ответ!)
Вы мне очень помогли. )))
Я подправил макрос под свою задачу и вот что получилось:
Источник
Vba excel сравнение файлов
Вот Вам инструмент: http://hugo.nxt.ru/CompareFiles.Find.rar , как раз для таких задач и делал.
В настройках пишите что сравнивать, куда копировать.
Да вот они, проверил, 17 совпадений:
Файл — приёмник: c:TempSnake_X.xls
Файл — источник: c:TempSnake_Y.xls
Столбцы сравнения в приёмнике: d,e,f
Столбцы сравнения в источнике: a,b,c
Лист — приёмник (№): 1
Лист — источник (№): 1
Столбцы — приёмники данных копирования: c,af,ad,ac
Столбцы — источники данных копирования: d,f,h,j
Option Explicit
Option Compare Text
Sub UpdateRecords ()
Dim wb As Workbook
Dim i As Integer , j As Integer , k As Integer
Dim LRx As Integer , LRy As Integer
‘Dim tm As Single
‘ tm = Timer
Application . ScreenUpdating = False
Set wb = GetObject ( ThisWorkbook . Path & «Y.xls» )
With wb . Sheets ( «SPISOK» )
LRx = Cells ( Rows . Count , 4 ). End ( xlUp ). Row
LRy = . Cells ( Rows . Count , 1 ). End ( xlUp ). Row
For i = 2 To LRx
For j = 2 To LRy
If Cells ( i , 4 ) = . Cells ( j , 1 ) And _
Cells ( i , 5 ) = . Cells ( j , 2 ) And _
Cells ( i , 6 ) = . Cells ( j , 3 ) Then
Cells ( i , 3 ) = . Cells ( j , 4 ): Cells ( i , 32 ) = . Cells ( j , 6 )
Cells ( i , 30 ) = . Cells ( j , 8 ): Cells ( i , 29 ) = . Cells ( j , 10 )
k = k + 1 : Exit For
End If
Next j : Next i
End With
wb . Close False
Application . ScreenUpdating = True
‘MsgBox Timer — tm
MsgBox «Обновлено записей: » & k, vbInformation, «Обновление»
End Sub
Источник
Vba excel сравнение файлов
Вот Вам инструмент: http://hugo.nxt.ru/CompareFiles.Find.rar , как раз для таких задач и делал.
В настройках пишите что сравнивать, куда копировать.
Да вот они, проверил, 17 совпадений:
Файл — приёмник: c:TempSnake_X.xls
Файл — источник: c:TempSnake_Y.xls
Столбцы сравнения в приёмнике: d,e,f
Столбцы сравнения в источнике: a,b,c
Лист — приёмник (№): 1
Лист — источник (№): 1
Столбцы — приёмники данных копирования: c,af,ad,ac
Столбцы — источники данных копирования: d,f,h,j
Option Explicit
Option Compare Text
Sub UpdateRecords ()
Dim wb As Workbook
Dim i As Integer , j As Integer , k As Integer
Dim LRx As Integer , LRy As Integer
‘Dim tm As Single
‘ tm = Timer
Application . ScreenUpdating = False
Set wb = GetObject ( ThisWorkbook . Path & «Y.xls» )
With wb . Sheets ( «SPISOK» )
LRx = Cells ( Rows . Count , 4 ). End ( xlUp ). Row
LRy = . Cells ( Rows . Count , 1 ). End ( xlUp ). Row
For i = 2 To LRx
For j = 2 To LRy
If Cells ( i , 4 ) = . Cells ( j , 1 ) And _
Cells ( i , 5 ) = . Cells ( j , 2 ) And _
Cells ( i , 6 ) = . Cells ( j , 3 ) Then
Cells ( i , 3 ) = . Cells ( j , 4 ): Cells ( i , 32 ) = . Cells ( j , 6 )
Cells ( i , 30 ) = . Cells ( j , 8 ): Cells ( i , 29 ) = . Cells ( j , 10 )
k = k + 1 : Exit For
End If
Next j : Next i
End With
wb . Close False
Application . ScreenUpdating = True
‘MsgBox Timer — tm
MsgBox «Обновлено записей: » & k, vbInformation, «Обновление»
End Sub
Источник
VBA Excel. Сравнение прайс-листов
Сравнение прайс-листов из кода VBA Excel с помощью массивов: сравнение номенклатуры, добавление новых позиций, корректировка цен на конкретном примере.
Данные для сравнения прайс-листов
Исходный прайс-лист (лист Price текущей рабочей книги ThisWorkbook ):
Поступивший прайс-лист (единственный лист файла Price1.xlsx ):
Имя файла с поступившим прайс-листом значения не имеет, так как мы будем его выбирать с помощью стандартного диалога выбора файлов.
Для фиксации результатов сравнения прайс-листов создадим новый лист NewPrice в книге ThisWorkbook и внесем в него информацию из исходного и поступившего прайс-листов:
- Добавим на лист NewPrice все позиции номенклатуры исходного листа Price и новые позиции из поступившего файла Price1.xlsx .
- На новом листе NewPrice заменим цены совпадающих позиций номенклатуры в исходном и поступившем листах на цены поступившего листа, увеличенные на 10% с округлением до рублей (сделаем наценку).
- Увеличим на листе NewPrice цены новых позиций на 10% с округлением до рублей (сделаем наценку).
- Скопируем форматы столбцов таблицы на листе Price в таблицу на листе NewPrice .
В результате должна получиться следующая таблица (без окончательной сортировки по алфавиту, чтобы были видны добавленные снизу новые позиции номенклатуры):
Для реализации кода сравнения прайс-листов в Excel будем использовать массивы, в которых циклы VBA работают намного быстрее, чем в диапазонах ячеек на рабочем листе.
Манипуляции с номенклатурой и ценами, описанные в списке выше, будут произведены в соответствующих массивах.
Сравнение прайс-листов в VBA Excel
Код VBA Excel для сравнения двух прайс-листов с подробными комментариями:
Источник
Вероника Куртова Пользователь Сообщений: 5 |
#1 09.11.2022 20:18:21 Здравствуйте! Подскажите пожалуйста,как можно переделать этот код?) Он сравнивает значение ячеек в одном файле. А мне нужно сравнить значение вот так:
т.е. в открытом файле сравнить знач ячейки из совсем другого файла…как этот код переделать?
|
||||
Вероника Куртова Пользователь Сообщений: 5 |
#2 09.11.2022 20:41:14 Может так вот?
|
||
Jack Famous Пользователь Сообщений: 10852 OS: Win 8.1 Корп. x64 | Excel 2016 x64: | Browser: Chrome |
#3 09.11.2022 20:49:27 Обе книги должны быть открыты. Не тестил.
попробуйте. Смысл тот же)) Изменено: Jack Famous — 09.11.2022 20:50:59 Во всех делах очень полезно периодически ставить знак вопроса к тому, что вы с давних пор считали не требующим доказательств (Бертран Рассел) ►Благодарности сюда◄ |
||
Забыла сказать, второй файл должен быть закрыт ((( |
|
New Пользователь Сообщений: 4582 |
Вероника Куртова, это тоже самое, что читать мысли человека на расстоянии… |
Я вас понимаю. Ну в целом задача такая… Открыт один фпйд, в котором идет сравнение значения определенной ячейки со значением определенной ячейки с другого, закрытого файла эксель, который находится на компьютере и если значения равны то скопировать значение ячейки м закрытого файла эксель. |
|
Пытливый Пользователь Сообщений: 4587 |
Пока второй файл «закрыт» программа о его существовании даже не догадывается, а вы хотите, чтобы она еще и внутрь этого, как вы изящно выразились, фпйд (точнее ведь и не скажешь! именно фпйд!) «заглянула», чего-то там сравнила и только при каких-то там условиях чего-то сделала. Кому решение нужно — тот пример и рисует. |
Вероника Куртова Пользователь Сообщений: 5 |
#8 09.11.2022 21:59:54
а как это незаметно от пользователя сделать? |
||
Ігор Гончаренко Пользователь Сообщений: 13746 |
#9 09.11.2022 22:04:31
так бы сразу и сказали, что вам обязательно с закрытого файла брать данные Программисты — это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете! |
||
Hugo Пользователь Сообщений: 23257 |
Ну можно ведь заюзать функционал формул или ExecuteExcel4Macro. Но имя листа знать всёж нужно. |
Ігор Гончаренко Пользователь Сообщений: 13746 |
#11 09.11.2022 22:29:21
нажмите Ctrl+Alt+Del Программисты — это люди, решающие проблемы, о существовании которых Вы не подозревали, методами, которых Вы не понимаете! |
||
New Пользователь Сообщений: 4582 |
#12 10.11.2022 03:35:17 Вероника Куртова, данные из закрытого файла никак не взять. Чтобы взять данные из файла — его обязательно надо открыть. Но совсем другое дело, что открыть файл, взять из него данные можно так, что пользователь сидя перед компьютером глазами ничего не увидит. Макросы умеют отключать (замораживать) экран на время работы. То есть можно взять данные из 100 файлов, сравнить эти данные, но человек запустивший макрос вообще ничего не увидит на экране, а лишь какой-то конечный итог, например, сообщение: «данные совпали!» Но файлы надо открывать, чтобы прочитать из них информацию и сделать сравнение взятых из них данных Изменено: New — 10.11.2022 03:36:52 |
This answer assumes that all Names in the data are unique. There is no
provision in this example to handle duplicate Names except to issue a
note in the debug output.
This answer will involve Dictionaries
. Please review this website for complete information on how and why they are an efficient way to store unique data. The short answer is that you can create a large dictionary by looking at a unique «key», which is a string that uniquely represents some data that you want to track. In your case, you’ve asserted that all of the Names are unique. Dictionaries exist for speedy access to any single entry — no looping through 200k entries to find the one you want. Use your unique key string and you have near-instant access to the data associated with that key.
For your situation, my example builds two Dictionaries
, one for each set of data. The keys are the Names. The values associated with each key (Name) is the row number on which each Name is used. The row numbers will play a major role later on.
As with the other Code Review for your previous version of code, I’ll reiterate:
- Identify your function parameters
ByRef
orByVal
- Declare your variables as close to their first use as possible.
As an example:
Dim arr1 As Variant
Dim arr2 As Variant
arr1 = BuildDataArrays(ws1, startRow:=2)
arr2 = BuildDataArrays(ws2, startRow:=2)
You’ll see that there is a call to a BuildDataArrays
function. That brings me to
- Functional Isolation. When your routine starts getting very long, that is the perfect time to begin breaking parts of the logic out into separate functions/subs. This is especially useful if you have repetitive logic where only the variable is different. This is the case for
BuildDataArrays
.
Give this function a worksheet and optionally the starting row or column and it determines the range of available data, returning a memory-based array. Breaking out code into separate routines is very helpful because it makes your main logic easier to follow.
Private Function BuildDataArrays(ByRef ws As Worksheet, _
Optional ByVal startRow As Long = 1, _
Optional ByVal startCol As Long = 1) As Variant
'--- assumes all rows and columns are valid data
Dim lastRow As Long
Dim lastCol As Long
Dim dataArea As Range
Dim data As Variant
With ws
lastRow = .Cells(.Rows.Count, startRow).End(xlUp).row
lastCol = .Cells(startCol, .Columns.Count).End(xlToLeft).Column
Set dataArea = .Cells(startRow, startCol).Resize(lastRow - startRow + 1, _
lastCol - startCol + 1)
data = dataArea
End With
BuildDataArrays = data
End Function
- Use
Dictionaries
to collect your data. As with the previous point, this is a perfect opportunity to isolate the logic in a separate function.
The BuildDataDictionary
function will accept your memory-based array and use the selected column of data as a unique key (currently defaulted to column «I»).
Private Function BuildDataDictionary(ByRef data As Variant, _
Optional ByVal keyColumn As Long = 8) As Dictionary
Dim row As Long
Dim name As String
Dim names As Dictionary
Set names = New Dictionary
For row = LBound(data, 1) To UBound(data, 1)
name = Trim$(data(row, keyColumn))
If Len(name) > 0 Then
If Not names.Exists(name) Then
'--- add the new name to the dictionary and save the row number
names.Add name, row
Else
'--- if you get here, it means that the Name is NOT unique
' and you'll have to change your logic, or change the name
Debug.Print "ERROR: Duplicate name detected on " & _
" on row " & row & ": '" & name & "'"
End If
End If
Next row
Set BuildDataDictionary = names
End Function
Next we’ll build the resulting report data. According to your description, the report will consist of all data rows (each with a unique Name), with any differences noted in the data itself. In your original post, you are assuming that the larger of the two row counts for the sheets will be your output array. This isn’t true.
Consider that, by definition, all of the data from Sheet1 is unique (because each row’s Name is unique). That means if you have 10 rows of data on Sheet1, your output data will have at least ten rows. It’s possible that your data on Sheet2 also has ten rows of data, and only one of those rows repeats a Name on Sheet1. So your resulting report of data will have 19 rows.
Dim totalRows As Long
totalRows = ws1Names.Count
'--- now add on the number of unique rows from the other sheet
Dim name As Variant
For Each name In ws2Names
If Not ws1Names.Exists(name) Then
'--- name is unique
totalRows = totalRows + 1
Else
'--- name is not unique
End If
Next name
Debug.Print "There are " & totalRows & " unique Names between the sheets"
'--- now build a correctly sized output array
' ASSUMES both arrays have the same number of columns!!
Dim reportData As Variant
ReDim reportData(1 To totalRows, 1 To UBound(arr1, 2))
Now that we’re about to generate the report data, we have to consider how to make note of any errors encountered. For this I’m using a Collection
, which is a simple way to generate a running list of items. In this case, for each difference I’m adding a string that notes the row and column of each difference in the data arrays. I can use this later on to highlight the difference cells.
'--- and create an object to list which cells are different
Dim diffCells As Collection
Set diffCells = New Collection
After that, we simply move the data over to the report array, making note of any differences.
'--- we know that all Names are unique in sheet1, so move the all that
' data from sheet1 into the report array
Dim row As Long
Dim col As Long
Dim ws1row As Long
Dim ws2row As Long
row = 1
For Each name In ws1Names
If ws2Names.Exists(name) Then
'--- this row will have a difference because the Names match!
' so get the rows for each sheet that match the name
ws1row = ws1Names(name)
ws2row = ws2Names(name)
For col = 1 To UBound(reportData, 2)
If arr1(ws1row, col) = arr2(ws2row, col) Then
reportData(row, col) = arr1(ws1row, col)
Else
'--- note the different values in the cell and add the
' row and column to the difference list
reportData(row, col) = arr1(ws1row, col) & " <> " & _
arr2(ws2row, col)
diffCells.Add CLng(row) & "," & CLng(col)
End If
Next col
Else
'--- this is a unique row, so a straight copy of all columns
For col = 1 To UBound(reportData, 2)
reportData(row, col) = arr1(row, col)
Next col
End If
row = row + 1
Next name
'--- the remaining data are the unique rows that exist in sheet2
' the "row" variable count is continued in this loop
For Each name In ws2Names
If Not ws1Names.Exists(name) Then
'--- this is a unique row, so a straight copy of all columns
ws2row = ws2Names(name)
For col = 1 To UBound(reportData, 2)
reportData(row, col) = arr2(ws2row, col)
Next col
row = row + 1
End If
Next name
The final step is to output the report data. In my example, I am not creating a new workbook, but only creating a new worksheet. You can un-comment some code lines there to change it back for your purposes.
If diffCells.Count > 0 Then
Dim report As Workbook
Dim reportWS As Worksheet
'Set report = Workbooks.Add 'un-comment to report to a new workbook
'Set reportWS = report.ActiveSheet 'un-comment to report to a new workbook
Set reportWS = ThisWorkbook.Sheets.Add 'comment to report to a new workbook
'--- copy the resulting report to the worksheet
Dim reportArea As Range
Set reportArea = reportWS.Range("A1").Resize(UBound(reportData, 1), UBound(reportData, 2))
With reportArea
.Value = reportData
.Columns("A:B").ColumnWidth = 25
'--- now highlight the cells that are different
Dim rowcol As Variant
Dim parts() As String
For Each rowcol In diffCells
parts = Split(rowcol, ",")
With .Cells(CLng(parts(0)), CLng(parts(1)))
.Font.Bold = True
.Font.ColorIndex = 3
End With
Next rowcol
End With
Debug.Print "Report Generated secs " & Timer - tm
End If
Here is the whole code module in one block:
Option Explicit
Sub test1()
Compare2WorkSheets Sheet1, Sheet2
End Sub
Sub Compare2WorkSheets(ByRef ws1 As Worksheet, ByRef ws2 As Worksheet)
Dim tm As Double
tm = Timer
'Application.ScreenUpdating = False
'Application.Calculation = xlCalculationManual
'Application.EnableEvents = False
'--- establish the data in the arrays, skip the header row
Dim arr1 As Variant
Dim arr2 As Variant
arr1 = BuildDataArrays(ws1, startRow:=2)
arr2 = BuildDataArrays(ws2, startRow:=2)
'--- buidl a dictionary of Names for each worksheet
Dim ws1Names As Dictionary
Dim ws2Names As Dictionary
Set ws1Names = BuildDataDictionary(arr1)
Set ws2Names = BuildDataDictionary(arr2)
'--- we don't know how many rows the report will be, so compare
' names between the two sheets to find out. it's basically
' the sum of the number of unique names between the sheets
Dim totalRows As Long
totalRows = ws1Names.Count
'--- now add on the number of unique rows from the other sheet
Dim name As Variant
For Each name In ws2Names
If Not ws1Names.Exists(name) Then
'--- name is unique
totalRows = totalRows + 1
Else
'--- name is not unique
End If
Next name
Debug.Print "There are " & totalRows & " unique Names between the sheets"
'--- now build a correctly sized output array
' ASSUMES both arrays have the same number of columns!!
Dim reportData As Variant
ReDim reportData(1 To totalRows, 1 To UBound(arr1, 2))
'--- and create an object to list which cells are different
Dim diffCells As Collection
Set diffCells = New Collection
'--- we know that all Names are unique in sheet1, so move the all that
' data from sheet1 into the report array
Dim row As Long
Dim col As Long
Dim ws1row As Long
Dim ws2row As Long
row = 1
For Each name In ws1Names
If ws2Names.Exists(name) Then
'--- this row will have a difference because the Names match!
' so get the rows for each sheet that match the name
ws1row = ws1Names(name)
ws2row = ws2Names(name)
For col = 1 To UBound(reportData, 2)
If arr1(ws1row, col) = arr2(ws2row, col) Then
reportData(row, col) = arr1(ws1row, col)
Else
'--- note the different values in the cell and add the
' row and column to the difference list
reportData(row, col) = arr1(ws1row, col) & " <> " & _
arr2(ws2row, col)
diffCells.Add CLng(row) & "," & CLng(col)
End If
Next col
Else
'--- this is a unique row, so a straight copy of all columns
For col = 1 To UBound(reportData, 2)
reportData(row, col) = arr1(row, col)
Next col
End If
row = row + 1
Next name
'--- the remaining data are the unique rows that exist in sheet2
' the "row" variable count is continued in this loop
For Each name In ws2Names
If Not ws1Names.Exists(name) Then
'--- this is a unique row, so a straight copy of all columns
ws2row = ws2Names(name)
For col = 1 To UBound(reportData, 2)
reportData(row, col) = arr2(ws2row, col)
Next col
row = row + 1
End If
Next name
Debug.Print " Calc secs " & Timer - tm
If diffCells.Count > 0 Then
Dim report As Workbook
Dim reportWS As Worksheet
'Set report = Workbooks.Add 'un-comment to report to a new workbook
'Set reportWS = report.ActiveSheet 'un-comment to report to a new workbook
Set reportWS = ThisWorkbook.Sheets.Add 'comment to report to a new workbook
'--- copy the resulting report to the worksheet
Dim reportArea As Range
Set reportArea = reportWS.Range("A1").Resize(UBound(reportData, 1), UBound(reportData, 2))
With reportArea
.Value = reportData
.Columns("A:B").ColumnWidth = 25
'--- now highlight the cells that are different
Dim rowcol As Variant
Dim parts() As String
For Each rowcol In diffCells
parts = Split(rowcol, ",")
With .Cells(CLng(parts(0)), CLng(parts(1)))
.Font.Bold = True
.Font.ColorIndex = 3
End With
Next rowcol
End With
Debug.Print "Report Generated secs " & Timer - tm
End If
'Application.ScreenUpdating = True
'Application.Calculation = xlCalculationAutomatic
'Application.EnableEvents = True
If diffCells.Count > 0 Then
Debug.Print diffCells.Count & " cells contain different data!"
Else
Debug.Print "No differences found between the sheets."
End If
End Sub
Private Function BuildDataArrays(ByRef ws As Worksheet, _
Optional ByVal startRow As Long = 1, _
Optional ByVal startCol As Long = 1) As Variant
'--- assumes all rows and columns are valid data
Dim lastRow As Long
Dim lastCol As Long
Dim dataArea As Range
Dim data As Variant
With ws
lastRow = .Cells(.Rows.Count, startRow).End(xlUp).row
lastCol = .Cells(startCol, .Columns.Count).End(xlToLeft).Column
Set dataArea = .Cells(startRow, startCol).Resize(lastRow - startRow + 1, _
lastCol - startCol + 1)
data = dataArea
End With
BuildDataArrays = data
End Function
Private Function BuildDataDictionary(ByRef data As Variant, _
Optional ByVal keyColumn As Long = 8) As Dictionary
Dim row As Long
Dim name As String
Dim names As Dictionary
Set names = New Dictionary
For row = LBound(data, 1) To UBound(data, 1)
name = Trim$(data(row, keyColumn))
If Len(name) > 0 Then
If Not names.Exists(name) Then
'--- add the new name to the dictionary and save the row number
names.Add name, row
Else
'--- if you get here, it means that the Name is NOT unique
' and you'll have to change your logic, or change the name
Debug.Print "ERROR: Duplicate name detected on " & _
" on row " & row & ": '" & name & "'"
End If
End If
Next row
Set BuildDataDictionary = names
End Function
EDIT: added an example on how to call the routine from a button click
It seems that you’re adding an ActiveX command button to your worksheet. In this case, the CommandButton1_Click()
method will be executed in the Sheet1 module. Take the code above with the Compare2WorkSheets
routine and paste it into a regular code module. Then, in your sheet1 module, fix up your button-click code like this:
Option Explicit
Private Sub CommandButton1_Click()
Dim myWorkbook1 As Workbook
Dim myWorkbook2 As Workbook
'--- if Sheet1 is contained in the workbook where the code is running, use this
Set myWorkbook1 = ThisWorkbook
'--- if Sheet1 is in a different -- but already open -- workbook, use this
Set myWorkbook1 = Workbooks("the-already-open-workbook-filename.xlsx")
'--- if Sheet1 is in a different -- but unopened -- workbook, use this
Set myWorkbook1 = Workbooks.Open("the-workbook-filename-to-open.xlsx")
'--- you can make the same decisions for setting myWorkbook2
Set myWorkbook2 = Workbooks.Open("C:Temptestreport1.xlsx")
Compare2WorkSheets myWorkbook1.Worksheets("Sheet1"), myWorkbook2.Worksheets("Sheet1")
myWorkbook1.Close
myWorkbook2.Close
End Sub
#excel #vba #comparison
Вопрос:
У меня есть два файла Excel под названием «Файл1» и «Файл2», которые содержат по два столбца, каждый из которых называется человеком, Адрес, если имя человека совпадает в обоих случаях, то необходимо сравнить адрес этого конкретного имени человека в обоих случаях, и необходимо выделить различия. Может ли кто-нибудь помочь мне с кодом VBA для этого
Комментарии:
1. Вы можете сделать это с помощью условного форматирования, используя формулу VLOOKUP
Ответ №1:
Сравнение столбцов ( Match
feat. Union
)
- Предполагается, что обе книги открыты.
- Отрегулируйте значения в разделе константы и цвета в конце кода.
- Принцип источника/назначения здесь имеет мало смысла, но я предпочитаю его нумерации.
- В двух словах, он будет проходить по ячейкам источника, пытаясь сопоставить ячейку в пункте назначения и проверяя значения, расположенные рядом с нужными ячейками. Если они не равны, будут выделены оба.
- С не найденными значениями ячеек ничего не произойдет.
Option Explicit
Sub highlightDifferences()
Const swbName As String = "File1.xlsx"
Const sName As String = "Sheet1"
Const sCols As String = "A:B"
Const sFirstRow As Long = 2
Const dwbName As String = "File2.xlsx"
Const dName As String = "Sheet2"
Const dCols As String = "A:B"
Const dFirstRow As Long = 2
Dim sws As Worksheet: Set sws = Workbooks(swbName).Worksheets(sName)
Dim srg As Range
Dim sCell As Range
With sws.Range(sCols).Rows(sFirstRow)
Set sCell = .Resize(.Worksheet.Rows.Count - .Row 1) _
.Find("*", , xlValues, , , xlPrevious)
If sCell Is Nothing Then Exit Sub
Set srg = .Resize(sCell.Row - .Row 1)
End With
Dim dws As Worksheet: Set dws = Workbooks(dwbName).Worksheets(dName)
Dim drg As Range:
With dws.Range(dCols).Rows(dFirstRow)
Dim dCell As Range
Set dCell = .Resize(.Worksheet.Rows.Count - .Row 1) _
.Find("*", , xlValues, , , xlPrevious)
If dCell Is Nothing Then Exit Sub
Set drg = .Resize(dCell.Row - .Row 1)
End With
Dim drg1 As Range: Set drg1 = drg.Columns(1)
Dim drg2 As Range: Set drg2 = drg.Columns(2)
Dim srgDel As Range
Dim drgDel As Range
Dim cIndex As Variant
For Each sCell In srg.Columns(1).Cells
cIndex = Application.Match(sCell.Value, drg1, 0)
If IsNumeric(cIndex) Then
If sCell.Offset(, 1).Value <> drg2.Cells(cIndex).Value Then
If srgDel Is Nothing Then
Set srgDel = sCell.Offset(, 1)
Set drgDel = drg2.Cells(cIndex)
Else
Set srgDel = Union(srgDel, sCell.Offset(, 1))
Set drgDel = Union(drgDel, drg2.Cells(cIndex))
End If
End If
End If
Next sCell
If Not srgDel Is Nothing Then
srgDel.Interior.Color = vbYellow
drgDel.Interior.Color = vbYellow
End If
End Sub
Комментарии:
1. Большое спасибо, что помогли мне его запустить и получили мой результат
Ответ №2:
Sub CompareAddresses()
Dim File1 As String
Dim File2 As String
Dim Sheetname1 As String
Dim Sheetname2 As String
Dim List1 As Variant
Dim List2 As Variant
Dim lastrow As Long
Dim DiffAddress1() As Boolean
Dim DiffAddress2() As Boolean
Dim a As Long
Dim b As Long
Dim firstRow As Integer
'Define Filepathes and Sheetnames
File1 = "C:ExcelFile1.xlsx"
File2 = "C:ExcelFile2.xlsx"
Sheetname1 = "NameList"
Sheetname2 = "NameList"
firstRow = 2 'Row in which the data starts in both sheets
'Open Files and load Data in Arrays
Workbooks.Open Filename:=File1
Windows(FilnameFromPath(File1)).activate
Sheets(Sheetname1).Select
lastrow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
List1 = Range("A1:B" amp; lastrow)
ReDim DiffAddress1(lastrow)
Workbooks.Open Filename:=File2
Windows(FilnameFromPath(File2)).activate
Sheets(Sheetname2).Select
lastrow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
List2 = Range("A1:B" amp; lastrow)
ReDim DiffAddress2(lastrow)
'Check for Differences in Data
For a = firstRow To UBound(List1, 1)
For b = firstRow To UBound(List2, 1)
If List1(a, 1) = List2(b, 1) Then
If Not List1(a, 2) = List2(b, 2) Then
DiffAddress1(a) = True
DiffAddress2(b) = True
End If
End If
Next b
Next a
'Mark Differences in Sheets with yellow background
Windows(FilnameFromPath(File1)).activate
Sheets(Sheetname1).Select
For a = firstRow To UBound(List1, 1)
If DiffAddress1(a) = True Then
Range("B" amp; a).Interior.Color = 65535
End If
Next a
Windows(FilnameFromPath(File2)).activate
Sheets(Sheetname2).Select
For a = firstRow To UBound(List2, 1)
If DiffAddress2(a) = True Then
Range("B" amp; a).Interior.Color = 65535
End If
Next a
End Sub
Public Function FilnameFromPath(FilePath As String) As String
Dim int_Pos As Integer
int_Pos = InStrRev(FilePath, "")
FilnameFromPath = Mid(FilePath, int_Pos 1, Len(FilePath) - int_Pos)
End Function