LINQ to Entities で sp_executesql になる/ならない(ADO.NET Entity Framework)

LINQ to Entities で次のように Where 句を記述したとします。

        Using ctx As New NorthwindEntities()
        Dim query = From p In northem.Products _
                   Where p.ProductID = 4 _
                   Select p.ProductID, p.ProductName
            For Each p In query
                Console.WriteLine(p.ProductID & ": " & p.ProductName)
            Next
        End Using

p.ProductID = 4 のように、値を LINQ クエリ内に埋め込んだ場合は、sp_executesql が利用されずに、次のように SQL が内部実行されてしまいます。

SELECT 
[Extent1].[ProductID] AS [ProductID], 
[Extent1].[ProductName] AS [ProductName]
FROM [dbo].[Products] AS [Extent1]
WHERE 4 = [Extent1].[ProductID]

このように sp_executesql が利用されない場合は、プロシージャ キャッシュ(実行プランが格納される SQL Server 内のメモリ領域)に Ad Hoc クエリとして登録されてしまうため(今回のケースでは単純なクエリなので自動パラメータ化も行われますが)、キャッシュ領域の無駄使いが発生する可能性が高くなるので、注意する必要があります。

しかし、通常は、値には変数を利用する場合が多いと思いますので、こういう使い方はほとんどしないと思いますが、値を直接埋め込んだ場合とそうでない場合に sp_executesql が利用される/されないが変化するということは、頭の片隅の留めておくことをお勧めします。


次のように、値に変数を利用する場合は、

            Dim pID As Integer = 4
            Dim query = From p In ctx.Products _
                        Where p.ProductID = pID _
                        Select p.ProductID, p.ProductName

内部実行される SQL は、

exec sp_executesql N'SELECT 
[Extent1].[ProductID] AS [ProductID], 
[Extent1].[ProductName] AS [ProductName], 
FROM [dbo].[Products] AS [Extent1]
WHERE [Extent1].[ProductID] = @p__linq__0',N'@p__linq__0 int',@p__linq__0=4

のように sp_executesql が実行されて、パラメーター化が行われるので、キャッシュの無駄使いにはなりません(実行プランの再利用性も高まります)。


なお、プロシージャ キャッシュの中身を参照する方法については、こちらへ投稿しています。
http://d.hatena.ne.jp/matu_tak/20090905/1255059685