2015-09-15 100 views
1

我繼承了設計不佳的數據庫表(沒有主鍵或索引,超大的nvarchar字段,日期存儲爲nvarchar等)。這張表格大約有350,000條記錄。我按照預定義的時間間隔遞交了大約2000個潛在新記錄的列表,並且如果數據庫還沒有匹配的記錄,我必須插入任何可能的新記錄。在c中比較非常大的數據庫對象列表

我最初嘗試在foreach循環中進行比較,但很快就很明顯,可能有更高效的方法。在做了一些研究之後,我嘗試了.Any().Contains().Exclude()方法。

我的研究使我相信.Exclude()方法將是最有效的方法,但在嘗試這種方法時會出現內存不足錯誤。 .Any().Contains()方法似乎都需要大致相同的時間來完成(這比foreach循環更快)。

兩個列表的結構是相同的,每個包含多個字符串。如果你不介意的話,我有幾個問題沒有找到令人滿意的答案。

  1. 當比較兩個對象列表(由多個字符串組成)時,.Exclude()方法被認爲是最有效的方法嗎?
  2. 使用.Exclude()方法時有沒有使用投影的方法?我想找到一種方法來完成會是這樣的:

    List<Data> storedData = db.Data; 
    List<Data> incomingData = someDataPreviouslyParsed; 
    
    // No Projection that runs out of memory 
    var newData = incomingData.Exclude(storedData).ToList(); 
    
    // PsudoCode that I would like to figure out if is possible 
    // First use projection on db so as to not get a bunch of irrelevant data 
    List<Data> storedData = db.Data.Select(x => new { x.field1, x.field2, x.field3 }); 
    var newData = incomingData.Select(x => new { x.field1, x.field2, x.field3 }).Exclude(storedData).ToList(); 
    
  3. 使用SQL Server工作室經理原始的SQL語句,查詢超過10秒的時間會稍長。使用EF,似乎需要超過一分鐘。這是由EF差勁優化的SQL,還是EF的開銷造成了這種差異?

  4. 在這種情況下,EF中的原始SQL會是更好的做法嗎?

半題外話: 當從數據庫中抓取數據並將其存儲在變量storedData,並能夠消除存儲在表中的任何索引的有用性(如果有任何)?

我不想問這麼多問題,而且我相信很多(如果不是全部的話)他們都不太好。但是,我無處可轉,我一直在尋找明確的答案。任何幫助非常讚賞。

UPDATE

經過進一步研究,我發現似乎是一個很好的解決了這個問題。使用EF,我從數據庫中獲取350,000條記錄,只保留創建唯一記錄所需的列。然後,我將這些數據轉換爲字典,將保留的列作爲關鍵字(如可以看到here)。這解決了返回的數據中已經存在重複的問題,並且使我能夠快速地將我新分析的數據與之進行比較。性能提升非常明顯!

我還不確定這是否會接近最佳實踐,但我當然可以接受這種表現。我也看到了一些對ToLookup()的引用,我可能會嘗試着去看看那裏是否有性能上的提升。不過,這裏是一些代碼來顯示我所做的:

var storedDataDictionary = storedData.GroupBy(k => (k.Field1 + k.Field2 + k.Field3 + k.Field4)).ToDictionary(g => g.Key, g => g.First()); 

foreach (var item in parsedData) 
{ 
    if (storedDataDictionary.ContainsKey(item.Field1 + item.Field2 + item.Field3 + item.Field4)) 
    { 
     // duplicateData is a previously defined list 
     duplicateData.Add(item); 
    } 
    else 
    { 
     // newData is a previously defined list 
     newData.Add(item); 
    } 
} 
+1

'350,000條記錄'似乎並不大。把所有的數據都存入內存,並用linq2objects完成你的工作....(當然,如果它只是一次性工作) – Eser

+0

我實際上已經使用上述幾種方法正確地運行了查詢。我更期待什麼可以被認爲是最有效的或最佳的做法。 – FlipperBizkut

+0

這取決於你的技能......我會用SQL做這樣的工作。 RDBMS完美地用於處理數據。如果允許改變結構,我會在一個好的桌子設計上投入一些思考,並首先將醜陋的數據轉移到新的結構中。如果你必須堅持這種結構,那麼RDBMS更適合處理大於RAM的數據。 – Shnugo

回答

0
  1. 沒有理由使用EF爲。

  2. 僅抓取需要更新或插入記錄的決策所需的列(以便表示缺少「主鍵」的那些列)。不要爲其他列浪費內存。

  3. 構建現有主鍵的HashSet(即,如果主鍵是一個數字,則爲int的HashSet,如果它有多個鍵 - 將它們組合爲字符串)。

  4. 檢查您的2000項對HashSet,這是非常快。

  5. 使用原始sql更新或插入項目。

+0

我應該說我正在使用ASP.NET並使用EF作爲我的ORM。因此,我使用EF從數據庫中獲取數據。不知道這是否明顯。對於上面的#3,你是說我應該抓住4個使記錄唯一的列,將它們連接起來形成單個字符串,然後創建該字符串的HashSet?如果DB中已經有重複的記錄(毫無疑問,會發生什麼)會發生什麼?將創建HashSet失敗?爲了比較HashSet,會使用一個foreach循環嗎?否則,如果不匹配,如何知道要插入哪條記錄? – FlipperBizkut

+0

至少對於這個特定的任務,你最好避免使用EF。 – Evk

0

我建議你考慮在SQL中進行,而不是C#。您沒有說出您正在使用的RDBMS,但您可以查看MERGE語句,例如(用於SQL Server 2008): https://technet.microsoft.com/en-us/library/bb522522%28v=sql.105%29.aspx

廣義上,該語句檢查記錄是否爲「新」 - 如果是,則可以將其插入;如果沒有UPDATE和DELETE功能,或者你忽略它。

+0

我正在使用SQL Server 2012.最終,這是可訪問數據庫服務器的Web應用程序的一部分。在當前條件下,無法直接訪問數據庫服務器以運行SQL命令(通過SQL Server Studio Manager等)。 – FlipperBizkut

+0

您不需要SSMS,只需使用您將用於插入的任何內容(例如Entity Framework或表適配器)。 SQL Server針對集合操作進行了優化。如果可能的話,編寫一個存儲過程以包含MERGE語句。該參數是您可能新記錄的列表。 [本網站](http://www.c-sharpcorner.com/UploadFile/vendettamit/entity-framework-4-and-tablevalue-type-in​​-storeprocedurefix/)包含一個示例。 –