Объедините две большие таблицы в одну таблицу на основе уникального идентификатора
Для начала, я немного знаю VBA и не пытался написать код для того, что я хочу сделать, потому что я даже не знаю, с чего начать.
В настоящее время у меня есть две таблицы. Таблица 1 содержит 48000 строк данных и два столбца, уникальный идентификатор и сумму наличных для каждого идентификатора. Таблица 2 содержит 50000 строк данных и два столбца, уникальный идентификатор и сумму наличных для каждого идентификатора. Идентификационные номера являются уникальными для их собственной таблицы, поэтому в другой таблице часто встречаются повторяющиеся идентификаторы. Цель этого состоит в том, чтобы объединить две таблицы по идентификационному номеру и показать общую сумму наличных для каждого идентификационного номера.
Моя первая попытка была связана с использованием функции SUMIF для получения итоговых сумм из обеих таблиц. Несмотря на то, что это работало для первого идентификатора, когда я попытался скопировать формулу в другие ячейки, мой ноутбук завис полностью, что привело к перезагрузке.
Моя вторая попытка состояла в использовании мастера сводных таблиц для объединения двух диапазонов. Однако я обнаружил, что сводные таблицы не могут обрабатывать столько уникальных значений. (Основываясь на появившемся всплывающем окне).
Моя третья попытка сработала, но я нашел это долго, и я надеюсь, что есть лучший метод. Я разделил свои таблицы на два диапазона по 20 000 строк (так что теперь есть 4 таблицы). Затем я использовал мастер сводных таблиц, чтобы объединить эти два элемента одновременно. Сначала были Таблица1 и Таблица3, затем Таблица2 и Таблица4. Затем мне пришлось снова разделить получающиеся списки, так как PivotTable не смог с этим справиться, и повторил этот процесс. Проблема этого метода в том, что я чувствую, что существует определенная вероятность пропущенных или повторяющихся значений из-за всего разделения.
Во время всех этих трех попыток у моего компьютера неоднократно возникали проблемы и требовалась перезагрузка.
Мне все равно, если решение VBA займет некоторое время, пока оно работает.
Я пытался просмотреть другие примеры, но некоторые я не мог понять, как применить их к моей ситуации, а другие, казалось, не работали с достаточно большими файлами, чтобы столкнуться с некоторыми проблемами, с которыми я сталкиваюсь.
Спасибо и, пожалуйста, дайте мне знать, если вам нужно что-то разъяснить.
4 ответа
В итоге я использовал мастер сводных таблиц, чтобы объединить диапазоны по 10 000.
Спасибо за помощь.
Я бы предложил подключиться к рабочим листам через соединение ADO и соединить две таблицы с помощью оператора SQL.
Добавьте ссылку на библиотеку объектов данных Microsoft ActiveX (Инструменты -> Ссылки...) - используйте последнюю версию, как правило, 6.1.
Вставьте модуль в проект VBA и вставьте следующий код:
Sub JoinTables()
Dim connectionString As String
connectionString = _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=""" & ActiveWorkbook.FullName & """;" & _
"Extended Properties=""Excel 12.0;HDR=Yes"""
'The SQL statement that shapes the resulting data
Dim sql As String
sql = _
"SELECT t1.ID, t1.Value + IIF(t2.Value IS NULL, 0, t2.Value) AS FinalSum " & _
"FROM [Sheet1$] AS t1 " & _
"LEFT JOIN [Sheet2$] AS t2 ON t1.ID = t2.ID " & _
"UNION SELECT t2.ID, t2.Value " & _
"FROM [Sheet2$] AS t2 " & _
"LEFT JOIN [Sheet1$] AS t1 ON t2.ID = t1.ID " & _
"WHERE t1.ID IS NULL"
Dim rs As New ADODB.Recordset
'All the fun happens here
rs.Open sql, connectionString
'Paste the resulting records into the third sheet of the active workbook
ActiveWorkbook.Sheets(3).Range("A2").CopyFromRecordset rs
Set rs = Nothing
End Sub
Заметки:
- В настоящее время набор записей читает данные из текущей (Excel) рабочей книги. Если данные поступают из базы данных, может быть проще и эффективнее изменить строку подключения для непосредственного подключения к базе данных и выполнить оператор SQL для базы данных.
- Код предполагает, что первая строка каждого листа содержит метки столбцов, например
ID
а такжеValue
, Если это не так, укажитеHDR=No
в третьей строкеconnectionString
(вместоHDR=Yes
), и поля будут автоматически назначаться имена, начиная сF1
,F2
, так далее. - Результаты вставляются в третий лист активной рабочей книги. Это может или не может быть уместным.
- Вы не указываете, как вы хотите упорядочить данные, но это достаточно просто с добавлением
ORDER BY
предложение к выражению SQL.
Объяснение оператора SQL
Мы сравниваем две таблицы. Для данного идентификатора может быть три возможности:
1. идентификатор появляется в обеих таблицах,
2. он появляется только в первой таблице, или
3. он появляется только во второй таблице.
Мы также работаем с предположением, что идентификатор уникален в каждой таблице.
Первая половина заявления (до UNION
) ручки 1 и 2.
SELECT t1.ID, t1.Value + IIF(t2.Value IS NULL, 0, t2.Value) AS FinalSum
FROM [Sheet1$] AS t1
LEFT JOIN [Sheet2$] AS t2 ON t1.ID = t2.ID
Это можно описать следующим образом:
Начните с записей в первой таблице -
FROM [Sheet1$] AS t1
Сопоставьте каждую запись во второй таблице с соответствующей записью в первой таблице на основе идентификатора -
LEFT JOIN [Sheet2$] AS t2 ON t1.ID = t2.ID
Включить все записи из первой таблицы и только соответствующие записи во второй таблице -
LEFT
вLEFT JOIN
Вернуть два столбца: идентификатор из первой таблицы и комбинацию значений из первой и второй таблицы -
SELECT ...
Если во второй таблице нет совпадающих записей, значение будет НЕДЕЙСТВИТЕЛЬНО (не равно нулю). Попытка добавить число в NULL вернет NULL, а это не то, что нам нужно. Итак, мы должны написать эту формулу -
t1.Value + IIF(t2.Value IS NULL, 0, t2.Value)
:
Если значение из второй таблицы равно нулю, добавьте 0
в противном случае добавьте значение из второй таблицы
Вторая половина оператора обрабатывает идентификаторы, которые появляются только во второй таблице:
UNION
SELECT t2.ID, t2.Value
FROM [Sheet2$] AS t2
LEFT JOIN [Sheet1$] AS t1 ON t2.ID = t1.ID
WHERE t1.ID IS NULL
Добавить второй набор результатов поверх первого набора результатов -
UNION
Начнем с записей из второй таблицы -
FROM [Sheet2$] AS t2
Сопоставьте записи из первой таблицы с записями во второй таблице (обратите внимание, что это обратное значение в первой половине запроса) -
LEFT JOIN [Sheet1$] AS t1 ON t2.ID = t1.ID
Нам нужны только записи, у которых нет идентификатора в первой таблице -
WHERE t1.ID IS NULL
Вот попытка получить отсортированную и комбинированную таблицу. Общая стратегия, которую я использовал здесь: создайте копии существующих таблиц и используйте их для добавления значений, удаления повторяющихся значений и сделайте то же самое для третьей объединенной таблицы на листе 3. Прикрепите следующий код к кнопке команды.
Application.ScreenUpdating = False
Dim i As Long, x As Long, n As Long, j As Long
Dim cashtotal As Integer
lastrow1 = Sheet1.Range("A1048575").End(xlUp).Row
astrow2 = Sheet2.Range("A1048575").End(xlUp).Row
cashtotal = 0
x = 1
'''''Routine to make a copy of the existing data.
For i = 1 To lastrow1
Sheet1.Cells(i, 4) = Sheet1.Cells(i, 1)
Sheet1.Cells(i, 5) = Sheet1.Cells(i, 2)
Next
'''''On Sheet1- Routine to remove repetitive values
For i = 2 To lastrow1
If Sheet1.Cells(i, 4) = "" Then GoTo 10
x = x + 1
cashtotal = Sheet1.Cells(i, 5)
Sheet1.Cells(x, 7) = Sheet1.Cells(i, 4)
Sheet1.Cells(x, 8) = Sheet1.Cells(i, 5)
For j = i + 1 To lastrow1
If Sheet1.Cells(j, 4) = Sheet1.Cells(i, 4) Then
cashtotal = cashtotal + Sheet1.Cells(j, 5)
Sheet1.Cells(x, 8) = cashtotal
Sheet1.Cells(j, 4).ClearContents
Sheet1.Cells(j, 5).ClearContents
End If
Next
10
Next
x = 1
'''''On Sheet2 the following routine makes a copy of the existing data
For i = 1 To lastrow2
Sheet2.Cells(i, 4) = Sheet2.Cells(i, 1)
Sheet2.Cells(i, 5) = Sheet2.Cells(i, 2)
Next
'''''On sheet2 - Routine to remove repetitive values
For i = 2 To lastrow2
If Sheet2.Cells(i, 4) = "" Then GoTo 20
x = x + 1
cashtotal = Sheet2.Cells(i, 5)
Sheet2.Cells(x, 7) = Sheet2.Cells(i, 4)
Sheet2.Cells(x, 8) = Sheet2.Cells(i, 5)
For j = i + 1 To lastrow2
If Sheet2.Cells(j, 4) = Sheet2.Cells(i, 4) Then
cashtotal = cashtotal + Sheet2.Cells(j, 5)
Sheet2.Cells(x, 8) = cashtotal
Sheet2.Cells(j, 4).ClearContents
Sheet2.Cells(j, 5).ClearContents
End If
Next
20
Next
x = 1
'''Transfer modified tables on sheet1 and sheet2 to sheet3 in a combined table
lastrow4 = Sheet1.Range("G1048575").End(xlUp).Row
For i = 1 To lastrow4
Sheet3.Cells(i, 1) = Sheet1.Cells(i, 7)
Sheet3.Cells(i, 2) = Sheet1.Cells(i, 8)
Next
lastrow5 = Sheet2.Range("G1048575").End(xlUp).Row
lastrow6 = Sheet3.Range("A1048575").End(xlUp).Row
For i = 2 To lastrow5
Sheet3.Cells(lastrow6 + i - 1, 1) = Sheet2.Cells(i, 7)
Sheet3.Cells(lastrow6 + i - 1, 2) = Sheet2.Cells(i, 8)
Next
'''''''Routine to make a copy of the existing table
lastrow7 = Sheet3.Range("A1048575").End(xlUp).Row
For i = 1 To lastrow7
Sheet3.Cells(i, 4) = Sheet3.Cells(i, 1)
Sheet3.Cells(i, 5) = Sheet3.Cells(i, 2)
Next
'''''''' Routine to remove repetitive values
For i = 2 To lastrow7
If Sheet3.Cells(i, 4) = "" Then GoTo 30
x = x + 1
cashtotal = Sheet3.Cells(i, 5)
Sheet3.Cells(x, 7) = Sheet3.Cells(i, 4)
Sheet3.Cells(x, 8) = Sheet3.Cells(i, 5)
For j = i + 1 To lastrow7
If Sheet3.Cells(j, 4) = Sheet3.Cells(i, 4) Then
cashtotal = cashtotal + Sheet3.Cells(j, 5)
Sheet3.Cells(x, 8) = cashtotal
Sheet3.Cells(j, 4).ClearContents
Sheet3.Cells(j, 5).ClearContents
End If
Next
30
Next
Application.ScreenUpdating = True
Если вам нужно решение VBA, в котором не используются сводные таблицы, вы можете попытаться создать объект словаря и использовать идентификатор в качестве ключа, а денежное значение - в качестве значения. как это. Сначала необходимо добавить ссылку на Microsoft Scripting Runtime.
Sub CreateEmployeeSum()
Dim wb As Workbook
Set wb = ThisWorkbook
Dim table1 As Worksheet, _
table2 As Worksheet, finalTable As Worksheet
'wasn't sure if you were using sheets of data
'or actual tables - if they are actual tables,
'you can loop through those in a similar way, look up
'on other stackru problems how
Set table1 = wb.Sheets("Sheet1") 'first sheet of info
Set table2 = wb.Sheets("Sheet2") 'second sheet of info
Set finalTable = wb.Sheets("Sheet3") 'destination sheet
'get the last row of both tables
Dim lastRowT1 As Long, lastRowT2 As Long
lastRowT1 = table1.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
lastRowT2 = table2.Cells.Find("*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
'write the info to arrays so faster to loop through
Dim t1Array As Variant, t2Array As Variant
t1Array = table1.Range("A1:B" & lastRowT2).Value
t2Array = table2.Range("A1:B" & lastRowT2).Value
'create a dictionary that maps IDs to cash value
Dim idToCashDict As Dictionary
Set idToCashDict = New Dictionary
'first loop through info from first sheet
Dim i As Long
For i = 1 To UBound(t1Array)
Dim idNum As String, cashVal As Double
idNum = CStr(t1Array(i, 1))
cashVal = CDbl(t1Array(i, 2))
If idToCashDict.Exists(idNum) Then
cashVal = cashVal + idToCashDict.Item(idNum)
idToCashDict.Remove idNum
idToCashDict.Add idNum, cashVal
Else
idToCashDict.Add idNum, cashVal
End If
Next i
'then through second sheet, adding to cash value of
'ids that have been seen before
For i = 1 To UBound(t2Array)
Dim idNum2 As String, cashVal2 As Double
idNum2 = CStr(t2Array(i, 1))
cashVal2 = CDbl(t2Array(i, 2))
If idToCashDict.Exists(idNum2) Then
cashVal2 = cashVal2 + idToCashDict.Item(idNum2)
idToCashDict.Remove idNum2
idToCashDict.Add idNum2, cashVal2
Else
idToCashDict.Add idNum2, cashVal2
End If
Next i
'then write the entries from the dictionary to the
'destination sheet
Dim finalVal As Double, finalID As String
i = 1
For Each finalID In idToCashDict.Keys
finalVal = idToCashDict.Item(finalID)
finalTable.Range("A" & i).Value = finalID
finalTable.Range("B" & i).Value = finalVal
i = i + 1
Next finalID
End Sub
Если вы используете реальные таблицы, посмотрите ответы, например, здесь, чтобы подобным образом пройтись по строкам.