2016-11-23 5 views
3

EFLinq to entitiesでいくつかのテストを行い、アプリケーションのパフォーマンスを向上させています。Linq to SQLによるエンティティ注文

私は説明できない奇妙なこと(私には)が気付くだけでなく、相当なオーバーヘッドが発生するかどうかも実際には分かりません。ここで

は私のLINQです:

var result = from n in query 
     orderby n.PersonId 
     select new 
     { 
      id = n.Id, 
      appointmentId = n.AppointmentId, 
      message = n.Message, 
      wasRead = n.Read, 
      canDismiss = (n.Appointment.Status != AppointmentStatus.Waiting), 
      date = n.IssueDateUtc 
     }; 

は、これは生成されたSQLです:

SELECT 
    [Project1].[Id] AS [Id], 
    [Project1].[AppointmentId] AS [AppointmentId], 
    [Project1].[Message] AS [Message], 
    [Project1].[Read] AS [Read], 
    [Project1].[C1] AS [C1], 
    [Project1].[IssueDateUtc] AS [IssueDateUtc] 
    FROM (SELECT 
     [Extent1].[Id] AS [Id], 
     [Extent1].[Read] AS [Read], 
     [Extent1].[Message] AS [Message], 
     [Extent1].[IssueDateUtc] AS [IssueDateUtc], 
     [Extent1].[AppointmentId] AS [AppointmentId], 
     [Extent1].[PersonId] AS [PersonId], 
     CASE WHEN (NOT ((1 = [Extent2].[Status]) AND ([Extent2].[Status] IS NOT NULL))) THEN cast(1 as bit) WHEN (1 = [Extent2].[Status]) THEN cast(0 as bit) END AS [C1] 
     FROM [dbo].[Notification] AS [Extent1] 
     LEFT OUTER JOIN [dbo].[Appointment] AS [Extent2] ON [Extent1].[AppointmentId] = [Extent2].[Id] 
     WHERE [Extent1].[PersonId] = @p__linq__0 
    ) AS [Project1] 
    **ORDER BY [Project1].[PersonId] ASC** 

これはそうしながら、私は別の投影(Project1)における基の種類結果をする必要性を理解していません

SELECT 
     [Extent1].[Id] AS [Id], 
     [Extent1].[Read] AS [Read], 
     [Extent1].[Message] AS [Message], 
     [Extent1].[IssueDateUtc] AS [IssueDateUtc], 
     [Extent1].[AppointmentId] AS [AppointmentId], 
     [Extent1].[PersonId] AS [PersonId], 
     CASE WHEN (NOT ((1 = [Extent2].[Status]) AND ([Extent2].[Status] IS NOT NULL))) THEN cast(1 as bit) WHEN (1 = [Extent2].[Status]) THEN cast(0 as bit) END AS [C1] 
     FROM [dbo].[Notification] AS [Extent1] 
     LEFT OUTER JOIN [dbo].[Appointment] AS [Extent2] ON [Extent1].[AppointmentId] = [Extent2].[Id] 
     WHERE [Extent1].[PersonId] = @p__linq__0 
     **ORDER BY [Extent1].[PersonId] ASC** 

私はかなりの量を見つけました。 efとlinqの両方によって疑わしいSQLが生成され、私は生のsqlsを書いていないほうがいいかどうか疑問に思っています。

質問です:生成されたSQLの余分なビットを心配する何かですか?なぜその投影が必要なのですか?

編集コメントで述べたように、新たなLINQ

に追加するには、多分、詳細は実行されて以降のクエリによって引き起こされました。私は1つのクエリオブジェクトを使用するためにLINQを書き直し、結果​​は同じです:

dbSet.Where(n => n.PersonId == id).Select(n => new 
      { 
       Id = n.Id, 
       AppointmentId = n.AppointmentId, 
       Message = n.Message, 
       Read = n.Read, 
       CanBeDismissed = (n.Appointment.Status != AppointmentStatus.Waiting), 
       IssueDate = n.IssueDateUtc 
      }).OrderBy(n => n.Id).ToList(); 

実行計画(同じ両方sqls用)

actual execution plan

編集2

このクエリは単純なカウントから取得しました。期待

dbSet.Count(x => x.Id == 1 && x.Read == false); 

SELECT 
[GroupBy1].[A1] AS [C1] 
FROM (SELECT 
    COUNT(1) AS [A1] 
    FROM [dbo].[Notification] AS [Extent1] 
    WHERE ([Extent1].[PersonId] = 19) AND (0 = [Extent1].[Read]) 
) AS [GroupBy1] 

SELECT 
    COUNT(1) AS [A1] 
    FROM [dbo].[Notification] AS [Extent1] 
    WHERE ([Extent1].[PersonId] = 19) AND (0 = [Extent1].[Read]) 

私はすべて、このラッパーはから来て、なぜされている場所を得ることはありません。

+0

私は 'query'は別のLINQツーSQLクエリであり、それはあなたが入れ子になった見たものであると仮定投影の内側にある。それはLINQでやっていることとまったく同じです。それを取り除きたいのであれば、 'OrderBy'と' Select'を 'query'に追加してください。しかし、後続のLINQクエリを使用してコードをきれいにしても、心配する必要はありません。投影には最小限のオーバーヘッドがあります。疑わしい場合は実行計画を比較してください。しかし、パフォーマンス上の問題がある場合は、その予測が原因ではありません。 –

+0

LINQ-to-SQLが生成するクエリに関する懸念... Linq-to-SQLは優れた(ただししばしば冗長な)SQLクエリを、複雑なシナリオではさらに多く生成しています。私はそれがほとんどのプログラマーが達成するよりも優れていると言いたい。もちろん、よく書かれたLINQクエリが必要です。だから、生成されたSQLをダブルチェックするのは良いことです...しかし、一般的には、LINQは一般的にジョインやクロスアプリケーションなどを使うときに非常に良い選択肢になります。 LINQの生成されたSQL;) –

+0

私は常に新しい何かを学んでいます。私はsgdbについて多くのことを学んだので、心配しています。私の製品は成長しています。今はdbアクセスの分析に時間を割いています。私はかなりひどいコードを見て、表現を書き直すだけで約60% 。とにかく、私は1つのクエリだけを使用してlinqを書き換えて、結果は同じでした。私は質問にそれを追加して、それをチェックしてください。私は余分なコードが使用されている匿名型のために生成されると思います。 – victor

答えて

1

小さなサンプルプロジェクトを私のマシンにまとめました。あなたの最初のサンプルで投影を起こす原因は、CanBeDismissedフィールドの条件付き計算であり、その結果、SQLではCASE WHENとなります。これを省略すると、Entity Frameworkは追加のプロジェクションを行いません。

ので、条件のチェックを:

db.Notifications 
    .Where(n => n.AppointmentId == 1) 
    .OrderBy(n => n.Id) 
    .Select(n => new 
    { 
     Id = n.Id, 
     Message = n.Message, 
     HasMessage = n.Message != null 
    }).ToList(); 

生成SQLは次のとおりです。

SELECT 
    [Project1].[Id] AS [Id], 
    [Project1].[Message] AS [Message], 
    [Project1].[C1] AS [C1] 
    FROM (SELECT 
     [Extent1].[Id] AS [Id], 
     [Extent1].[Message] AS [Message], 
     CASE WHEN ([Extent1].[Message] IS NOT NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C1] 
     FROM [dbo].[Notifications] AS [Extent1] 
     WHERE 1 = [Extent1].[AppointmentId] 
    ) AS [Project1] 
    ORDER BY [Project1].[Id] ASC 

私は、後で参照するために結果の実行計画を追加してみましょう:

Execution plan with projection

場合あなたはそれを残す:

db.Notifications 
    .Where(n => n.AppointmentId == 1) 
    .OrderBy(n => n.Id) 
    .Select(n => new 
    { 
     Id = n.Id, 
     Message = n.Message 
    }).ToList(); 

ませ投影はEFによって行われません:

SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[Message] AS [Message] 
    FROM [dbo].[Notifications] AS [Extent1] 
    WHERE 1 = [Extent1].[AppointmentId] 
    ORDER BY [Extent1].[Id] ASC 

だから、これが理由です。 countサンプルにも同じことが適用されます。グループ化が発生している場合、EFはクエリをより冗長にする追加の投影法を追加します。しかし、重要な点は、あなたの質問へのコメントで述べたように、はパフォーマンスを傷つけません、この追加の投影について心配する必要はありません。

今私はちょうど最初のクエリからpojectionを取り除き、内側のクエリにORDERBYを移動した次のクエリの実行プランを追加することによって、私は証拠このましょう:

SELECT 
[Extent1].[Id] AS [Id], 
[Extent1].[Message] AS [Message], 
CASE WHEN ([Extent1].[Message] IS NOT NULL) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C1] 
FROM [dbo].[Notifications] AS [Extent1] 
WHERE 1 = [Extent1].[AppointmentId] 
ORDER BY [Extent1].[Id] ASC 

Execution plan wihtout projection

これはまったく同じです。追加のタスクは追加されず、コスト配分も変わりません。 SQLクエリオプティマイザは、このようなプロジェクトを最適に最適化します。

また、予測について心配することはありません。彼らはあなたを傷つけることはありませんが、私はそれらが見え、時には不必要に冗長になることに同意します。あなたはあなたが投稿実行計画にClustered Index Scanがなぜ起こるかを見て、あなたのクエリでパフォーマンスの問題が発生した場合、

まず:

パフォーマンスの問題:しかし、ここであなたを助けるかもしれない二つのことがあります。これはインデックス作成の問題の兆候ではありませんが、それは非常に頻繁です。あなたの問題はここで根付くかもしれません。

unneccesaryの突起を取り除く:

あなたはまだ、すべてのものの投影を取り除きたい(あるいは少なくともそれ以上)の場合例は、Entity Frameworkのコア1.0があります - それは実際よりも、よりよいSQLを生成EF 6に移行することを検討する価値はあるかもしれませんが、EF 6が提供するすべての機能が搭載されていないことに注意してください。EF Core 1.0が提供していない機能を使用している場合、しかし、それは完全な.NET Framework 4.xで動作します!

ここEFコア1.0は、私は私の答えの最初のLINQ文を実行したときに生成するものの例です:

SELECT [n].[Id], [n].[Message], CASE 
    WHEN [n].[Message] IS NULL 
    THEN CAST(0 AS BIT) ELSE CAST(1 AS BIT) 
END 
FROM [Notifications] AS [n] 
WHERE ([n].[Id] = 1) AND ([n].[Id] = 1) 
ORDER BY [n].[Id] 
+0

非常に良い、ありがとう。私はインデックスのスキャンは、すべての行がそのfk列がXであるかどうかをチェックする必要があるために起こると思います。この状態が解決された後、実行計画でDBがインデックスを実行してID Xを持つ行を取得して結合を実行しようとしているように見えます。私はこれが正しいと思う。確かではない。 EFは場合によっては余分な投影法を追加する必要があると思われます。 – victor