2009-03-31 3 views
2

私はSQLServerデータベースにヒットするためのさまざまな方法のプロファイリングを行っていました。私は、バニラTSQL、CompiledQuery、およびコンパイルされていないLinqステートメントを行った。CompiledQueryとPlain Linq SQLの生成

予想されたとおりにパフォーマンスが同じ順番で行われましたが、後者の2つをプロファイリングするときに気になることがありました。

CompiledQueryによって生成されたSQLは、普通の古い文で生成されたものよりも優れていました。

ローカルSQLExpressデータベース。テーブルは 'foreignTable'と呼ばれ、ColumnAはint、主キー(インデックス)です。 ColumnBはランダムなintです。

Func<testingDatabaseEntities1, int, int> GetByPK = CompiledQuery.Compile((testingDatabaseEntities1 ft, int key) 
    => (ft.foreignTable.Where(x => x.ColumnA == key).FirstOrDefault().ColumnB)); 

は、生成されたコードのために、本当にあまりにもひどいないが、

SELECT 
[Project1].[ColumnB] AS [ColumnB] 
FROM (SELECT cast(1 as bit) AS X) AS [SingleRowTable1] 
LEFT OUTER JOIN (SELECT TOP (1) 
    [Extent1].[ColumnB] AS [ColumnB] 
    FROM [dbo].[foreignTable] AS [Extent1] 
    WHERE [Extent1].[ColumnA] = @p__linq__1) AS [Project1] ON 1 = 1 

を生成します。

しかし、私は無地のLINQ文の実行するとき:

entity.foreignTable.Where(x => x.ColumnA == searchForMe).FirstOrDefault().ColumnB 

それが生成する:ちょうど安っぽいです

SELECT 
[Limit1].[C1] AS [C1], 
[Limit1].[ColumnA] AS [ColumnA], 
[Limit1].[ColumnB] AS [ColumnB], 
[Limit1].[FKColumn] AS [FKColumn] 
FROM (SELECT TOP (1) 
    [Extent1].[ColumnA] AS [ColumnA], 
    [Extent1].[ColumnB] AS [ColumnB], 
    [Extent2].[FKColumn] AS [FKColumn], 
    1 AS [C1] 
    FROM [dbo].[foreignTable] AS [Extent1] 
    LEFT OUTER JOIN (SELECT 
     [Table_2].[FKColumn] AS [FKColumn], 
     [Table_2].[SomeText] AS [SomeText] 
     FROM [dbo].[Table_2] AS [Table_2]) AS [Extent2] ON [Extent1].[ColumnA] = [Extent2].[FKColumn] 
    WHERE [Extent1].[ColumnA] = @p__linq__7 
) AS [Limit1] 

を。

私は疑問に思っています。通常のLinqにエンティティに同じ量のCompiledQueryと同じ量のSQLを与えることは可能ですか?

答えて

5

比較しているクエリが同じではありません。最初にコンパイルされたクエリは1つのプロパティを返し、それ以外のものは返しません。それは決して異なるものを返すことはできません。 2番目のインスタンスは、逆参照したエンティティインスタンスを返します。クエリが実行されると、1つのプロパティのみを表示することを意識する方法がありません。

var b = (from e in entity.foreignTable. 
     where ColumnA == searchForMe 
     select new 
     { 
      ColumnB = e.ColumnB 
     }).FirstOrDefault().ColumnB; 
+0

何の要素がない場合ので、null参照をスローします:あなたは非コンパイルされたクエリからの同じSQLを取得することができるかもしれません(未テスト)

一つの方法は、匿名型に投影することです見つかりました。私の答えに投稿したバージョンは、その動作をしません。 – eglasius

+0

はい。それは意図的です。私は彼の質問でクエリの動作に従っていた。あなたの答えは異なって振る舞います。 –

+0

OPサンプルのような.FirstOrDefault +列アクセスでコンパイルされたクエリを使用すると少し奇妙なので、実際に投げたかどうかはわかりませんでした。あなたのバージョンや鉱山のいずれかを使用するようコンパイルされたバージョンをリファクタリングします。たとえそれらが長くなっても、意図が何であるかをもっとはっきりさせることができます。 – eglasius

3

コンパイルされたクエリは、そのクエリを複数回使用することが予想されるため、余分な作業をしています。それがあなたの後ろのものなら、コンパイルされたクエリに固執するだけです。

この特定のシナリオでは、あなたがしたい:

entity.foreignTable 
    .Where(x => x.ColumnA == searchForMe) 
    .Select(x=>x.ColumnB) 
    .FirstOrDefault(); 

これはまったく同じではありませんが、それは確かに多くのあなたが(だけColumnBを取得するために)期待のクエリを行います。 FirstOrDefaultを実行するとインスタンスが生成されるため、linq2sqlクエリの後に.ColumnBが実行されています。これは、null参照のためにバージョンが失敗するため、この新しいクエリによってColumnBのデフォルト(nullまたはColumnB定義に応じた特定の値)が与えられるため、動作の違いが発生します。

関連する問題