是否有比Enumerable更快的东西。除了<tsource>方法?

时间:2017-10-20 08:05:44

标签: sql-server vb.net linq bulkinsert bulkupdate

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

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

但问题是当用户选择特定时间段(每个重新编码已创建数据时间)时,程序必须比较两个表并在两个表中划分重新编码(服务器数据)。一个是,不存在数据,第二个不是现有数据。而我要做的是, 不是现有数据直接插入客户端数据库(我使用批量插入)和现有数据使用bulkcopy插入临时表,并在使用上述临时表更新客户端表后。划分服务器表时出现了实际问题。这就是我做的方式

 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的重新编码)

当有超过1个Milion重新编码时,更新部分需要10分钟的可接受时间(是的,从Ram获取5GB空间 - 在这种情况下,在考虑性能时可以) 但插入部分接缝需要数天,只是为了配合insertTable(数据表)。这是问题。
AsEnumerable().Except()部分花了很长时间,我找不到解决方案加速这个过程。我不确定我是否正确解释了这一点。有人可以给我一些建议吗?

1 个答案:

答案 0 :(得分:1)

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