2012-01-04 36 views
3

我遇到了一個問題,不知道如何解決它。想象一下,你有一個List<int>,它有大約6000個唯一的id,它與表中的id匹配,在sql數據表中有大約一百萬條記錄。我想從我的c#程序中選擇那些通過LINQ匹配這些id的6000條記錄。我不想使用Contains(),因爲它翻譯得非常慢,參數列表變得很大。LINQ - 通過WHERE子句查詢6000條獨特記錄

任何其他的想法如何解決這個問題?


一些關於我的情況(這是不是真實的,但類似的情景):

我有一個連接到數據庫的服務。客戶請求一批像「人」這樣的項目。服務接受請求,查詢數據庫並將數據發送回客戶端。

人=(是PersonID,女士prename,姓氏)

現在客戶持有人的臨時列表。用另一種方法,我想從服務中檢索這些人的地址。所以我把一個PersonID列表放到服務中,這個服務應該給我一個有關這些人的引用的地址列表。

+1

該ID的子集可能是連續的?例如[1-5,10-12,14,19-32] – dtb 2012-01-04 17:14:40

+0

有沒有辦法改變你的查詢,這樣你就不必發送這個ID了(就像有一個連接,或者你可以使用的子查詢你解決服務器上的6,000個ID)? – JMarsch 2012-01-04 17:16:37

+0

如果從另一個表中選擇這些6000個ID,那麼爲什麼不寫一個SQL存儲過程使用這些ID並將存儲過程的結果返回給客戶端? – 2012-01-04 17:23:31

回答

3

如果您正在使用實體框架(EF),您可以使用您的數據(數據)和表格(客戶)之間的內部連接

void Main() 
{  
    var data = Enumerable.Range(1, 6000); 

    var result = from x in data 
    join y in Customers 
    on x equals y.CustomerID 
    select x; 

    result.Dump(); 
} 
+0

我的確在使用EF。如果這真的起作用,那將非常棒!我想我剛纔已經閱讀過類似的解決方案。我會試試這個 – KroaX 2012-01-04 17:36:03

+0

我提供的代碼正在工作,我只是從LINQPad上運行它。 – 2012-01-04 17:37:01

+0

你知道它在技術上的工作原理嗎? sql命令是怎麼樣的? – KroaX 2012-01-04 17:41:12

5

我不會推薦這個。與LINQ一樣,它也是一種很棒的工具,但有些情況下,通過處理代碼來試圖更好地處理數據,這對於應用程序的性能是非常不利的。

你已經得到了這些Id的列表,如果它們在數據庫中,那麼爲什麼不把整個操作作爲存儲過程來做,只是返回結果,這樣你就不必推動昂貴的查詢整個網絡中,全部都在數據庫中,因此您可以最大限度地減少流量,並可能提高響應速度。

6000個項目似乎並沒有像這樣打擾很多,但實際上如您所說,在嘗試使用大小的數據集進行選擇時可能會帶來一點性能上的惡夢。

+1

另外,如果它們已經在數據庫中,只需通過LINQ模型暴露該表/ proc並「加入」它即可。這會讓你得到你正在尋找的合併結果。如果沒有 - 我同意馬修 - 一個自定義的過程來加載一個表變量和手動執行SQL連接,可能會好很多。 – jklemmack 2012-01-04 17:25:57

3

一些想法:

插入6000點的ID到一個臨時表,並加入該臨時表的萬人次的紀錄之一。

使用Contains()並分批次選擇n,其中N = 500,1000等,而不是一次全部6000。

使用Contains()將使linq創建一個非常大的SQL語句。

+0

你打我+1。很久以前,我們不得不在ADO.net中使用類似的技術(使用批處理)。那時候,我們做了一些試驗,發現批處理中的1,000個項目是臨界點(對於Oracle或SQL Server,不記得哪一個)。如果您將參數列表保留在1000以下,則表現非常好。它每次大大降低1000以上。 – JMarsch 2012-01-04 17:18:28

+0

當你說「table」時,你的意思是一個SQL臨時表還是一個C#的對象列表?如果你建議在SQL端做這件事,你會不會失去集合論的好處?我可以看到像這樣的負載下的LINQ緊張,但SQL引擎已針對大型設置查詢進行了優化... – CamronBute 2012-01-04 17:20:57

+0

@JMarsch - 我們也遇到了發現這種情況的痛苦。我工作的一個應用程序創建了動態SQL(pre-linq),其中一些contains子句非常大,或者最終會耗盡內存。您可以增加查詢內存使用量作爲臨時權宜之計,但最終我們使用臨時表進行了重新設計以克服限制。 – 2012-01-04 17:22:44

1

通常情況下,我發現xml最適合用於ID的大型IN標準。它還可以獲得SQL Server中最大2100個參數,如果在LINQ中執行Contains,那麼您將遇到這個參數。

我建議:

  • 使列表
  • 序列化成XML
  • 創建一個名爲ContainsXYZ一個存儲過程,需要將XML作爲參數
  • 有你的存儲過程使用XPath提取Ids並加入它
  • 假設您正在使用實體框架,您可以映射此存儲過程,執行它,然後將結果實現爲常規實體。
+0

我可以證實,XML解析器是從參數中讀取數組的最快方式,與解析逗號或空格分隔字符串相比較。 – 2015-12-15 08:11:59

0

只是爲了得到這一點的方式...

var joinTry = from company in dc.Companies 
       join id in list on company.CompanyID equals id 
       select company; 

不起作用。 LinqToSql不會讓你加入。 「本地序列不能用於除Contains運算符之外的查詢運算符的LINQ to SQL實現。」

var containsTry = from company in dc.Companies 
        where list.Contains(company.CompanyID) 
        select company; 

確實有效。在可預見的

SELECT [t0].[CompanyID], [t0].[CompanyName] 
FROM [Company] AS [t0] 
WHERE [t0].[CompanyID] IN (@p0, @p1, @p2, @p3, @p4, @p5, ... 

方式......爲髒,因爲這是,有沒有認真更快的方法來獲得整數列表到SQL Server。任何調用的開銷時間比任何解析都要嚴重得多。

SELECT 
    c.CompanyId, 
    c.CompanyName 
FROM Company c 
    WHERE CompanyID IN (1,2,3,4,5,6,7,8,9,10) 

...是相同的執行速度...(由LINQ生成)

exec sp_executesql N'SELECT [t0].[CompanyID], [t0].[CompanyName] 
FROM [Company] AS [t0] 
WHERE [t0].[CompanyID] IN (@p0, @p1, @p2, @p3, @p4, @p5, @p6, @p7, @p8, @p9)',N'@p0 int,@p1 int,@p2 int,@p3 int,@p4 int,@p5 int,@p6 int,@p7 int,@p8 int,@p9 int',@p0=1,@p1=2,@p2=3,@p3=4,@p4=5,@p5=6,@p6=7,@p7=8,@p8=9,@p9=10 

...和是快兩倍...

SELECT 
    c.CompanyId, 
    c.CompanyName 
FROM Company c 
     /* @Test is a table variable with 1-10 in it */ 
    INNER JOIN @Test t ON t.ID = c.CompanyID 

您實際上不需要優化SQL Server對整數列表的處理。在IN()解決方案中,SQL將整數放入它隨時隨地生成的索引中。

真正的問題應該是......「我用6000個整數列表代表什麼?」和「我應該把這份清單放在桌子上嗎?」。任何採用6000個整數的客戶端列表並將其發送到服務器的解決方案,與使用Contains()的解決方案相比,開銷>>。如果你使用LinqToSQL,你必須在某種程度上出售給範式。

如果這仍然讓你覺得髒亂,你可以嘗試爲任意限制列表創建一個表。兩列,兩個整數。然後,您可以將您的ID插入該表中,然後只需使用...

var searchTry = from company in dc.Companies 
       join search in dc.SearchLists on company.CompanyID equals search.ValueID 
       where search.SearchID == savedSearchID 
       select company; 
+0

你的假設是錯誤的。你的第一個案件有效,但你必須在另一個方向做。 來自列表中的ID 加入公司在dc.company上id等於companys.id select companys; – KroaX 2012-01-05 08:10:52

+0

@KroaX,不,你錯了!如果您想使用轉換爲SQL而不是從數據庫中選擇所有記錄,那麼您可以使用EF數據集來啓動LINQ表達式,然後加入其他任何內容。從列表中選擇,然後加入EF DbSet,您將強制選擇所有記錄! – 2015-12-15 08:06:06