2012-09-14 64 views
9

我使用實體框架和我需要檢查是否有名稱=「XYZ」的產品存在...LINQ到實體 - 任何VS首先VS已存在

我想我可以用任何(),是否存在( )或First()。

哪一個是這種情況的最佳選擇?哪一個表現最好?

謝謝你,

米格爾

回答

15

任何在數據庫級別轉換爲「Exists」。首先轉換爲Select Top 1 ...之間,Exists將執行First,因爲實際對象不需要提取,只有布爾結果值。

至少你沒有問過.Where(x => x.Count()> 0),它需要對整個匹配集進行評估和迭代,然後才能確定你有一條記錄。任何短路的請求,可以顯着更快。

+3

+1。澄清:統計整個匹配集是在數據庫級完成的,因此它不會將對象提取到應用程序中。仍然'.Any()'更簡單,它將勝過'.Where(x => x.Count> 0)',所以它應該是首選。 – StriplingWarrior

+0

@StriplingWarrior在數據庫中完成計數是事實,但由於EF的可怕查詢生成,它仍然比計數還要糟糕。看到我的答案。 –

1

Any()First()使用與IEnumerable它給你懶洋洋地評價事情的靈活性。但是Exists()需要列表。

我希望這可以爲您解決問題並幫助您決定使用哪一個。

2

有人會認爲Any()給出了更好的結果,因爲它轉化爲一個EXISTS查詢......但EF是非常破碎,產生這種(編輯):

SELECT 
CASE WHEN (EXISTS (SELECT 
    1 AS [C1] 
    FROM [MyTable] AS [Extent1] 
    WHERE Condition 
)) THEN cast(1 as bit) WHEN (NOT EXISTS (SELECT 
    1 AS [C1] 
    FROM [MyTable] AS [Extent2] 
    WHERE Condition 
)) THEN cast(0 as bit) END AS [C1] 
FROM (SELECT 1 AS X) AS [SingleRowTable1] 

相反的:

SELECT 
CASE WHEN (EXISTS (SELECT 
    1 AS [C1] 
    FROM [MyTable] AS [Extent1] 
    WHERE Condition 
)) THEN cast(1 as bit) 
    ELSE cast(0 as bit) END AS [C1] 
FROM (SELECT 1 AS X) AS [SingleRowTable1] 

...基本上加倍查詢成本(對於簡單查詢;對於複雜查詢更是如此)

我發現使用.Count(condition) > 0幾乎總是更快(成本與正確書寫的EXISTS查詢完全一樣)

+1

一個有趣的觀察,但我恐怕這個答案過於簡單化的事情到可能誤導開始點。 [看我的回答](http://stackoverflow.com/a/12439909/120955)。 – StriplingWarrior

22

好的,我不打算對此加以考慮,但迭戈的回答讓事情變得複雜化,我認爲有些額外的解釋一切順利。

在大多數情況下,.Any()會更快。以下是一些例子。

Workflows.Where(w => w.Activities.Any()) 
Workflows.Where(w => w.Activities.Any(a => a.Title == "xyz")) 

在上面的兩個例子中,Entity Framework產生了一個最佳查詢。 .Any()調用是謂詞的一部分,而Entity Framework處理得很好。但是,如果我們的結果集這樣的.Any()部分結果:

Workflows.Select(w => w.Activities.Any(a => a.Title == "xyz")) 

...突然實體框架決定創建條件的兩個版本,因此查詢不盡可能多的工作,它的兩倍真的需要。但是,下面的查詢是沒有任何好轉:

Workflows.Select(w => w.Activities.Count(a => a.Title == "xyz") > 0) 

鑑於上述情況查詢,實體框架仍然會創造條件的兩個版本,再加上它也將需要的SQL Server做一個實際計數,這意味着它一旦發現物品就不會短路。

但如果你只是比較這兩個查詢:

  1. Activities.Any(a => a.Title == "xyz")
  2. Activities.Count(a => a.Title == "xyz") > 0

...這將是更快?這取決於。

第一個查詢產生一個低效率的雙重條件查詢,這意味着它將花費兩倍的時間。

第二個查詢強制數據庫檢查表中沒有短路的每個項目,這意味着它最多可能需要比其所需時間長N,這取決於在找到匹配之前需要評估多少項目。讓我們假設該表有10,000項:

  • 如果表中沒有項目的條件相匹配,這個查詢將採取大約一半的時間作爲第一個查詢。
  • 如果表格中的第一項與條件匹配,則該查詢將比第一個查詢花費大約5,000倍的
  • 如果表格中的一個項目匹配,則該查詢將比第一個查詢花費的時間要長,比平均延長2,500倍
  • 如果查詢能夠利用Title和關鍵列上的索引,則該查詢將花費大約一半的時間作爲第一個查詢的

因此,在總結,如果你是:

  1. 使用 實體框架4(因爲新版本可能會提高查詢結構) 實體框架6.1或更早版本(因爲6.1.1 has a fix to improve the query),和
  2. 直接查詢表格(而不是查詢子查詢),並且直接使用結果(而不是作爲謂詞的一部分),並且
  3. Eit她說:
    1. 你有很好的指標設置上要查詢的表,或者
    2. 你希望不被發現的大部分時間項目

那麼你可以指望.Any()要花費兩倍於.Count()。例如,查詢可能需要100毫秒而不是50,或者10而不是5。

的其他情形.Any()至少應該儘可能快,並可能數量級的速度比.Count()

無論,直到您確定這實際上是產品性能不佳的原因,您應該更關心易於理解的內容。 .Any()更清楚簡明地說明你真正想要弄清楚什麼,所以堅持這一點。

+0

如果您試圖解決Any()錯誤(?),而不是將其轉換爲'Count()> 0',我會使用'FirstOrDefault()!= null'。 – Mormegil

+0

@Mormegil:一個有趣的想法。但是我測試了它,並且創建了一個更瘋狂的查詢,並帶有兩個外部應用的select語句。所以它實際上並沒有解決問題,並且它使代碼(在我看來)略微不那麼清晰。 – StriplingWarrior

+3

+1「,除非你確定這實際上是產品性能低下的根源,你應該更關心容易理解的東西。」評論 –