Есть ли что-то быстрее, чем метод Enumerable.Except<TSource>?
У меня есть программа, которая загружает данные из базы данных сервера в базу данных клиента. база данных сервера продолжает расти в последнее время.
в этой программе есть возможность выбрать загрузку всех данных или загрузку данных за определенный период времени (можно выбрать отсталые дни с сегодняшнего дня). если пользователь выбирает все, я написал программу для усечения таблицы базы данных клиента и вставки всех данных с помощью массового копирования. эта часть в порядке.
но проблема в том, что когда пользователь выбирает определенный период времени (каждый код перекодировки создает время данных), программа должна сравнить две таблицы и разделить коды перекодировки (данные сервера) на две таблицы. один - это не существующие данные, а второй - не существующие данные. и то, что я собираюсь сделать, это - не существующие данные напрямую вставлять в клиентскую БД (я использую массовую вставку) и вставку существующих данных в таблицу временных данных с помощью массового копирования и после обновления клиентской таблицы с использованием вышеуказанной временной таблицы. Моя настоящая проблема возникает при разделении таблицы сервера. вот как я это сделал
updateTable = (From c In dt_from_server.AsEnumerable()
Join o In Dt_from_client.AsEnumerable()
On c.Field(Of String)("BARCODE").Trim() Equals o.Field(Of String)("BARCODE").Trim()
And c.Field(Of String)("ITEM_CODE").Trim() Equals o.Field(Of String)("ITEM_CODE").Trim()
Select c).CopyToDataTable()
insertTable = dt_server.AsEnumerable()
.Except(updateTable.AsEnumerable(), DataRowComparer.Default)
.CopyToDataTable()
(обычно более 1М перекодирует в таблице серверов)
когда выполняется более 1 Милиона, Обновление детали занимает приемлемое время, например, 10 минут (Да, это занимает 5 ГБ пространства у Рама - в этом случае это нормально при рассмотрении производительности), но вставка швов детали занимает несколько дней, просто чтобы вставить insertTable(datatable), это проблема.AsEnumerable().Except()
участие занимало много времени, и я не мог найти решение, ускоряющее этот процесс. Я не уверен, что объяснил это правильно. Может ли кто-нибудь дать мне совет для этого?
1 ответ
Так как вы прокомментировали, что dt_from_server
а также dt_server
на самом деле то же самое DataTable
вам не нужно сравнивать все значения всех DataRows
друг с другом, вот что DataRowComparer.Default
делает. Ты можешь использовать Except
без второго параметра для компаратора сравниваются только ссылки, что намного быстрее.
Вам также не нужно два CopyToDataTable
который создает два дополнительных больших DataTables
в памяти, обрабатывать строки одну за другой.
Вот другой подход, использующий лево-внешнее соединение Linq, который более эффективен:
Dim query = from rServ in dt_from_server.AsEnumerable()
group join rClient in Dt_from_client.AsEnumerable()
On New With{
Key .BarCode = rServ.Field(Of String)("BARCODE").Trim(),
Key .ItemCode = rServ.Field(Of String)("ITEM_CODE").Trim()
} Equals New With{
Key .BarCode = rClient.Field(Of String)("BARCODE").Trim(),
Key .ItemCode = rClient.Field(Of String)("ITEM_CODE").Trim()
} into Group
From client In Group.DefaultIfEmpty()
Select new With { .ServerRow = rServ, .InsertRow = client is Nothing }
Dim insertOrUpdateRows = query.ToLookup(Function(x) x.InsertRow, Function(x) x.ServerRow)
Dim insertRows = insertOrUpdateRows(true).CopyToDataTable() 'CopyToDataTable redundant if you process rows immediately now'
Dim updateRows = insertOrUpdateRows(false).CopyToDataTable() 'CopyToDataTable redundant if you process rows immediately now'
Но в целом наиболее масштабируемым и эффективным подходом было бы не загружать все в память сразу, а затем обрабатывать все, а использовать подкачку базы данных (или хранимую процедуру) для обработки только ее части в памяти, в противном случае, вероятно, вы встретит OutOfMemoryException
рано или поздно.
C# по запросу:
var query = from rServ in dt_from_server.AsEnumerable()
join rClient in Dt_from_client.AsEnumerable()
on new { BarCode = rServ.Field<string>("BARCODE").Trim(), ItemCode = rServ.Field<string>("ITEM_CODE").Trim() }
equals new { BarCode = rClient.Field<string>("BARCODE").Trim(), ItemCode = rClient.Field<string>("ITEM_CODE").Trim() }
into clientGroup
from client in clientGroup.DefaultIfEmpty()
select new { ServerRow = rServ, InsertRow = client == null };
var insertOrUpdateRows = query.ToLookup(x => x.InsertRow, x => x.ServerRow);
var insertRows = insertOrUpdateRows[true].CopyToDataTable(); // CopyToDataTable redundant if you process rows immediately now
var updateRows = insertOrUpdateRows[false].CopyToDataTable(); // CopyToDataTable redundant if you process rows immediately now