4

今天我發現Entity Framework正在爲它生成的SQL添加一個不必要的子查詢。我開始挖掘我的代碼,試圖縮小它可能來自哪裏。 (很長),後來我指出了造成它的原因。但是現在我比開始時更困惑,因爲我不知道爲什麼會導致它。EF 4.1:爲什麼將常量變成變量會導致額外的子查詢?

基本上我發現,在某些情況下,只需將常量轉換爲變量就可以改變實體框架生成的SQL。我已經縮水一切到最低限度,並在一個小控制檯應用程序包裝它:

using System; 
using System.Data.Entity; 
using System.Linq; 

class Program 
{ 
    private static readonly BlogContext _db = new BlogContext(); 

    static void Main(string[] args) 
    { 
     const string email = "[email protected]"; 

     var comments = from c in _db.Comments 
         where c.Email == email 
         select c; 

     var result = (from p in _db.Posts 
         join c in comments on p.PostId equals c.PostId 
         orderby p.Title 
         select new { p.Title, c.Content }); 

     Console.WriteLine(result); 
    } 
} 

public class BlogContext : DbContext 
{ 
    public DbSet<Post> Posts { get; set; } 
    public DbSet<Comment> Comments { get; set; } 
} 

public class Post 
{ 
    public int PostId { get; set; } 
    public string Title { get; set; } 
} 

public class Comment 
{ 
    public int CommentId { get; set; } 
    public int PostId { get; set; } 
    public string Email { get; set; } 
    public string Content { get; set; } 
} 

這說明下面的輸出,這是完美的:

SELECT 
[Extent1].[PostId] AS [PostId], 
[Extent1].[Title] AS [Title], 
[Extent2].[Content] AS [Content] 
FROM [dbo].[Posts] AS [Extent1] 
INNER JOIN [dbo].[Comments] AS [Extent2] ON [Extent1].[PostId] = [Extent2].[PostId] 
WHERE N'[email protected]' = [Extent2].[Email] 
ORDER BY [Extent1].[Title] ASC 

現在,如果我做email一個變量:

/*const*/ string email = "[email protected]"; 

輸出變化根本:

SELECT 
[Project1].[PostId] AS [PostId], 
[Project1].[Title] AS [Title], 
[Project1].[Content] AS [Content] 
FROM (SELECT 
     [Extent1].[PostId] AS [PostId], 
     [Extent1].[Title] AS [Title], 
     [Extent2].[Content] AS [Content] 
     FROM [dbo].[Posts] AS [Extent1] 
     INNER JOIN [dbo].[Comments] AS [Extent2] ON [Extent1].[PostId] = [Extent2].[PostId] 
     WHERE [Extent2].[Email] = @p__linq__0 
) AS [Project1] 
ORDER BY [Project1].[Title] ASC 

請注意,LINQ to SQL似乎沒有這樣做。我知道忽略這個可能是可以的,因爲兩個命令都返回相同的數據。但我非常好奇爲什麼會發生這種情況。直到今天,我總是有(也許是錯誤的)印象,即把常量變成變量總是安全的,只要值保持不變(在這種情況下)。所以我必須問...

爲什麼一個看似微不足道的變化會在生成的SQL中造成如此大的差異?

更新:

只是要清楚,我的問題是不是email是第一個查詢和硬編碼值在第二(可變的,這使得所有的檢測值在世界上)。我的問題是關於爲什麼變量版本導致額外的子查詢。

謝謝!

+0

我不擔心查詢的*形式* SQL的意思是關於表達你想要的結果,而不是實現它們的過程 - 而且如果有很多或任何差異,我會感到驚訝在查詢計劃中。優化器應該產生很多相同的查詢計劃。 – 2011-05-07 16:03:28

+0

這是一個迷人的發現!僅僅因爲這個原因Upvoted! – 2015-04-18 20:00:34

回答

1

這實際上是SQL中的一個很大的區別嗎?內部查詢與原始查詢相同,而外部查詢只是內部的不會更改結果集的封裝。

除非這是造成問題,我個人不會擔心它。這兩種查詢的查詢計劃是否有所不同?我的猜測是他們是相同的。

+0

謝謝!那麼,在這種情況下,差異可能可以忽略不計。但是我正在處理的實際代碼是一個*很大的更大應用程序的數據訪問層,它使IQueryables遍佈各處。我只是擔心,如果像這樣的小改動導致生成額外的查詢,那麼可能更大的更改最終會導致未來一些令人討厭的T-SQL混亂。我關心這個的主要原因是因爲我喜歡使用LINQ並計劃繼續使用它(當事情變得混亂時不必訴諸流水線)。 – 2011-05-07 16:12:44

+1

@Daniel - 雖然你實際上並沒有在這裏獲得額外的查詢,你仍然只有_one_查詢 - 儘管稍微複雜一點的查詢。 SQL Server不會優化掉查詢的外部部分 - 誠實! – 2011-05-07 16:17:04

+0

是的,我知道數據庫只被擊中一次,是的,我真的很想相信SQL Server足夠聰明,可以確定外部查詢是多餘的。我只是不能幫助自己,需要問,並確保:) – 2011-05-07 16:29:35

1

就像人們說的。兩個查詢之間的差異很小。

原因是,當您創建LINQ時創建的表達式在使用變量和常量時不同。而EF會抓住這一點,並會按照你的要求生成你的SQL。它知道它永遠不會改變,所以它可以被硬編碼到查詢中以獲得(可能的)性能增益。

編輯: 我不認爲有這個問題的答案,除了「那是EF如何做」。但是EF很喜歡創建很多子選擇,這是衆所周知的。對於更復雜的查詢,它可能導致許多子查詢。有些人甚至反對甚至使用EF來處理這個事實。但這僅僅是使用像EF這樣的工具的價格。你放鬆了對某些東西的細粒度控制,這可以帶來巨大的性能提升。爲什麼使用.NET,何時可以使用C並獲得更多性能?爲什麼在使用組件時使用C,以獲得更多性能增益?

只有安全的方法,仍然能夠使用高抽象層EF是經常使用SQL profiller,並檢查是否有沒有查詢,在實際數據上花費太長時間。如果你找到一些,那麼要麼將它們轉化爲指導SQL或存儲過程。

+0

謝謝Euphoric。是的,我注意到使用該常量時該值是硬編碼的。這我理解,這是非常合理的。我的問題是關於子查詢。 – 2011-05-07 16:16:08

4

答案相當簡單。您的LINQ查詢用表達式樹表示。常量變量與非常量變量的區別在於ConstantExpressionParameterExpression

當你使用const時,你的LINQ查詢使用ConstExpression作爲這個變量,當你使用非const時,它使用了由EF運行時解釋不同的​​。

常量實際上意味着該值永遠不會改變,並且該值可以內聯到查詢中。

+0

這讓我想起我仍然有很多東西需要了解一般的LINQ以及特別是表達式樹。 Ivan非常感謝這些鏈接。 – 2011-05-07 16:25:07

+0

-1。根據編輯不回答。 – Euphoric 2011-05-07 16:30:34

+4

@Euphoric:在問題(之後)被編輯之前,看起來正確的回答很難回答 – BrokenGlass 2011-05-07 16:33:36

3

不是問題的答案 - 只是使用參數的上下文。

這是關於創建一個查詢,以便它將重新使用現有的查詢計劃。

如果將變量(而不是對參數的引用)注入生成SQL,那麼當變量發生更改時,SQL Server(以及其他數據庫引擎)將無法重新使用相同的計劃。

對於常量,這不是問題,因爲您知道該值始終相同,但對於變量,每次執行查詢時SQL和查詢計劃都會稍有不同。

這可能聽起來不太多,但SQL只有一定數量的空間分配給查詢計劃,因此緩存中有成百上千的微小變化是真正的「浪費空間」!

+1

+1我認爲這是問題的答案。此外,對於最大的EF奧祕之一,它看起來非常清晰。 – 2011-05-07 21:52:08

相關問題