有没有比 Enumerable.Except<TSource> 方法更快的方法?

is there something faster than Enumerable.Except<TSource> Method?

我有一个程序可以将数据从服务器数据库下载到客户端数据库。服务器数据库最近不断增长。

在该程序中,有一个选项可以 select 下载所有数据或下载特定时间段的数据(可以 select 从今天开始倒退几天)。如果用户 select 全部,我编写了程序来截断客户端数据库 table 并使用批量复制插入所有数据。那部分没问题。

但问题是当用户 select 特定时间段(每个重新编码创建数据时间)程序必须比较两个 table 并将重新编码(服务器数据)分成两个 table秒。一个是不存在的数据,第二个是不存在的数据。我要做的是 现有数据不直接插入客户端数据库(我正在使用批量插入),现有数据使用批量复制插入临时 table 并在更新客户端 table 后使用上述临时 table。我的实际问题发生在划分服务器 table 时。我就是这样做的

 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()

(正常情况下服务器有超过1M的recodes table )

当超过 100 万次重新编码时,更新部分需要 acceptable 大约 10 分钟的时间(是的,它需要 5GB space 来自 Ram - 在这种情况下,考虑性能时没问题) 但是插入零件接缝需要几天时间,只是为了评估 insertTable(datatable)。就是这个问题。
AsEnumerable().Except() 部分需要很长时间,我找不到加速此过程的解决方案。我不确定我是否正确解释了这一点。谁能给我一些建议吗?

由于您评论说 dt_from_serverdt_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