2017-02-23 6 views
2

私たちはEntity Frameworkを使用しており、簡単なLEFT JOINを実行したいと考えています。実際にEFでこのLEFT JOINクエリを書く方法

、これは我々がLINQ(照会可能)にしたいと思いSQLです:

SELECT 
    cbl.ID as ClaimBatchLine_ID 
    ,cbl.PurchasePrice 
    ,c.* 
    ,ic.DueDate 
    ,ic.Reference 
FROM ClaimBatchLine cbl 
INNER JOIN Claim c ON c.ID = cbl.CLaim_ID 
LEFT JOIN InvoiceClaim ic ON ic.ID = c.ID 
WHERE cbl.ClaimBatch_ID = @claimBatchId 
ORDER BY cbl.ID 
OFFSET (@recordsPerPage*@page) ROWS 
FETCH NEXT @recordsPerPage ROWS ONLY 

私たちが思い付いたことはこれです:

from cbl in ClaimBatchLines where cbl.ClaimBatch_ID == 1 
from c in Claims where c.ID == cbl.Claim_ID 
from ic in InvoiceClaims.DefaultIfEmpty() where ic.ID == c.ID 
select new {cbl, c, ic.Reference} 

そして、それは次のように生成SQL。

SELECT [t0].[ID], 
    [t0].[ClaimBatch_ID], 
    [t0].[Claim_ID], 
    [t0].[PurchasePrice], 
    [t1].[ID] AS [ID2], 
    [t1].[ClaimType_ID], 
    [t1].[Debtor_ID], 
    [t1].[CustomerContractRevision_ID], 
    [t1].[Date], 
    [t1].[CreatedOn], 
    [t1].[GrossAmount], 
    [t1].[OpenAmount], 
    [t1].[IsProcessedByOpenAmountCalculator], 
    [t1].[RowVersion], 
    [t2].[Reference] AS [Reference] 
FROM [ClaimBatchLine] AS [t0] 
    CROSS JOIN [Claim] AS [t1] 
        LEFT OUTER JOIN [InvoiceClaim] AS [t2] ON 1 = 1 
WHERE([t2].[ID] = [t1].[ID]) 
    AND ([t1].[ID] = [t0].[Claim_ID]) 
    AND ([t0].[ClaimBatch_ID] = @p0); 

同じ結果セットが生成されます。それは素晴らしいです。しかし、あなたが見ることができるようにLEFT OUTER JOIN [InvoiceClaim] AS [t2] ON 1 = 1は私たちが望むものではありません。代わりにLEFT JOIN InvoiceClaim ic ON ic.ID = c.IDに翻訳していただければ幸いです。

何か間違っていますか?または、LINQ to SQLは、パフォーマンスに関しては最適以下であり、われわれが望むものを理解することはできません。

EDIT:LINQPadで これはいくつかの素敵なクエリ

from cbl in ClaimBatchLines 
join c in Claims on cbl.Claim_ID equals c.ID 
join ic in InvoiceClaims on c.ID equals ic.ID into g 
from e in g.DefaultIfEmpty() 
where cbl.ClaimBatch_ID == 1 
select new {cbl, c, e.Reference} 

すなわち

-- Region Parameters 
DECLARE @p0 INT= 1; 
-- EndRegion 
SELECT [t0].[ID], 
    [Columns left out for brevity] 
    [t2].[Reference] AS [Reference] 
FROM [ClaimBatchLine] AS [t0] 
    INNER JOIN [Claim] AS [t1] ON [t0].[Claim_ID] = [t1].[ID] 
    LEFT OUTER JOIN [InvoiceClaim] AS [t2] ON [t1].[ID] = [t2].[ID] 
WHERE [t0].[ClaimBatch_ID] = @p0; 

になりますがそのようなページング機能を追加するとき:

(from cbl in ClaimBatchLines 
join c in Claims on cbl.Claim_ID equals c.ID 
join ic in InvoiceClaims on c.ID equals ic.ID into g 
from e in g.DefaultIfEmpty() 
where cbl.ClaimBatch_ID == 1 
select new {cbl, c, e.Reference}) 
.OrderBy(a => a.cbl.ID) 
.Skip(0 * 15000) 
.Take(15000) 

をそれが生成しますこの「モンスター」:

-- Region Parameters 
DECLARE @p0 INT= 1; 
DECLARE @p1 INT= 0; 
DECLARE @p2 INT= 15000; 
-- EndRegion 
SELECT [t4].[ID], 
    [Columsn left out for brevity...] 
FROM 
(
    SELECT ROW_NUMBER() OVER(ORDER BY [t3].[ID]) AS [ROW_NUMBER], 
     [Columsn left out for brevity...] 
    FROM 
    (
     SELECT [t0].[ID], 
      [Columsn left out for brevity...] 
     FROM [ClaimBatchLine] AS [t0] 
      INNER JOIN [Claim] AS [t1] ON [t0].[Claim_ID] = [t1].[ID] 
      LEFT OUTER JOIN [InvoiceClaim] AS [t2] ON [t1].[ID] = [t2].[ID] 
    ) AS [t3] 
    WHERE [t3].[ClaimBatch_ID] = @p0 
) AS [t4] 
WHERE [t4].[ROW_NUMBER] BETWEEN @p1 + 1 AND @p1 + @p2 
ORDER BY [t4].[ROW_NUMBER]; 

さらに悪いことに、私は同じLINT-TO-EFないLINQpad経由ではなく、コードでは、EFのリポジトリを使用していることを実行すると、私はこのさらに大きなモンスターを得る:

SELECT [Project5].[ID] AS [ID], 
    [Columns left out for brevity...] 
    [Project5].[Reference] AS [Reference] 
FROM 
(
    SELECT [Extent1].[ID] AS [ID], 
     [Extent1].[Claim_ID] AS [Claim_ID], 
     [Extent1].[ClaimBatch_ID] AS [ClaimBatch_ID], 
     [Extent1].[PurchasePrice] AS [PurchasePrice], 
     [Join4].[Id1] AS [ID1], 
     [Join4].[ClaimType_ID] AS [ClaimType_ID], 
     [Join4].[Debtor_ID] AS [Debtor_ID], 
     [Join4].[CustomerContractRevision_ID] AS [CustomerContractRevision_ID], 
     [Join4].[Date] AS [Date], 
     [Join4].[GrossAmount] AS [GrossAmount], 
     [Join4].[OpenAmount] AS [OpenAmount], 
     [Join4].[CreatedOn] AS [CreatedOn], 
     [Join4].[IsProcessedByOpenAmountCalculator] AS [IsProcessedByOpenAmountCalculator], 
     CASE 
      WHEN((NOT(([Join4].[C11] = 1) 
        AND ([Join4].[C11] IS NOT NULL))) 
       AND (NOT(([Join4].[C12] = 1) 
         AND ([Join4].[C12] IS NOT NULL))) 
       AND (NOT(([Join4].[C13] = 1) 
         AND ([Join4].[C13] IS NOT NULL))) 
       AND (NOT(([Join4].[C14] = 1) 
         AND ([Join4].[C14] IS NOT NULL)))) 
      THEN '2X' 
      WHEN(([Join4].[C14] = 1) 
       AND ([Join4].[C14] IS NOT NULL)) 
      THEN '2X0X' 
      WHEN(([Join4].[C12] = 1) 
       AND ([Join4].[C12] IS NOT NULL)) 
      THEN '2X1X' 
      WHEN(([Join4].[C11] = 1) 
       AND ([Join4].[C11] IS NOT NULL)) 
      THEN '2X2X' 
      ELSE '2X3X' 
     END AS [C1], 
     CASE 
      WHEN((NOT(([Join4].[C11] = 1) 
        AND ([Join4].[C11] IS NOT NULL))) 
       AND (NOT(([Join4].[C12] = 1) 
         AND ([Join4].[C12] IS NOT NULL))) 
       AND (NOT(([Join4].[C13] = 1) 
         AND ([Join4].[C13] IS NOT NULL))) 
       AND (NOT(([Join4].[C14] = 1) 
         AND ([Join4].[C14] IS NOT NULL)))) 
      THEN CAST(NULL AS BIT) 
      WHEN(([Join4].[C14] = 1) 
       AND ([Join4].[C14] IS NOT NULL)) 
      THEN [Join4].[IsAppeared] 
      WHEN(([Join4].[C12] = 1) 
       AND ([Join4].[C12] IS NOT NULL)) 
      THEN CAST(NULL AS BIT) 
      WHEN(([Join4].[C11] = 1) 
       AND ([Join4].[C11] IS NOT NULL)) 
      THEN CAST(NULL AS BIT) 
     END AS [C2], 
     CASE 
      WHEN((NOT(([Join4].[C11] = 1) 
        AND ([Join4].[C11] IS NOT NULL))) 
       AND (NOT(([Join4].[C12] = 1) 
         AND ([Join4].[C12] IS NOT NULL))) 
       AND (NOT(([Join4].[C13] = 1) 
         AND ([Join4].[C13] IS NOT NULL))) 
       AND (NOT(([Join4].[C14] = 1) 
         AND ([Join4].[C14] IS NOT NULL)))) 
      THEN CAST(NULL AS TINYINT) 
      WHEN(([Join4].[C14] = 1) 
       AND ([Join4].[C14] IS NOT NULL)) 
      THEN CAST(NULL AS TINYINT) 
      WHEN(([Join4].[C12] = 1) 
       AND ([Join4].[C12] IS NOT NULL)) 
      THEN [Join4].[AdjustmentClaimReason_ID] 
      WHEN(([Join4].[C11] = 1) 
       AND ([Join4].[C11] IS NOT NULL)) 
      THEN CAST(NULL AS TINYINT) 
     END AS [C3], 
     CASE 
      WHEN((NOT(([Join4].[C11] = 1) 
        AND ([Join4].[C11] IS NOT NULL))) 
       AND (NOT(([Join4].[C12] = 1) 
         AND ([Join4].[C12] IS NOT NULL))) 
       AND (NOT(([Join4].[C13] = 1) 
         AND ([Join4].[C13] IS NOT NULL))) 
       AND (NOT(([Join4].[C14] = 1) 
         AND ([Join4].[C14] IS NOT NULL)))) 
      THEN CAST(NULL AS INT) 
      WHEN(([Join4].[C14] = 1) 
       AND ([Join4].[C14] IS NOT NULL)) 
      THEN CAST(NULL AS INT) 
      WHEN(([Join4].[C12] = 1) 
       AND ([Join4].[C12] IS NOT NULL)) 
      THEN [Join4].[User_ID] 
      WHEN(([Join4].[C11] = 1) 
       AND ([Join4].[C11] IS NOT NULL)) 
      THEN CAST(NULL AS INT) 
     END AS [C4], 
     CASE 
      WHEN((NOT(([Join4].[C11] = 1) 
        AND ([Join4].[C11] IS NOT NULL))) 
       AND (NOT(([Join4].[C12] = 1) 
         AND ([Join4].[C12] IS NOT NULL))) 
       AND (NOT(([Join4].[C13] = 1) 
         AND ([Join4].[C13] IS NOT NULL))) 
       AND (NOT(([Join4].[C14] = 1) 
         AND ([Join4].[C14] IS NOT NULL)))) 
      THEN CAST(NULL AS INT) 
      WHEN(([Join4].[C14] = 1) 
       AND ([Join4].[C14] IS NOT NULL)) 
      THEN CAST(NULL AS INT) 
      WHEN(([Join4].[C12] = 1) 
       AND ([Join4].[C12] IS NOT NULL)) 
      THEN CAST(NULL AS INT) 
      WHEN(([Join4].[C11] = 1) 
       AND ([Join4].[C11] IS NOT NULL)) 
      THEN [Join4].[CostClaimAnnouncement_ID] 
     END AS [C5], 
     CASE 
      WHEN((NOT(([Join4].[C11] = 1) 
        AND ([Join4].[C11] IS NOT NULL))) 
       AND (NOT(([Join4].[C12] = 1) 
         AND ([Join4].[C12] IS NOT NULL))) 
       AND (NOT(([Join4].[C13] = 1) 
         AND ([Join4].[C13] IS NOT NULL))) 
       AND (NOT(([Join4].[C14] = 1) 
         AND ([Join4].[C14] IS NOT NULL)))) 
      THEN CAST(NULL AS DECIMAL(19, 4)) 
      WHEN(([Join4].[C14] = 1) 
       AND ([Join4].[C14] IS NOT NULL)) 
      THEN CAST(NULL AS DECIMAL(19, 4)) 
      WHEN(([Join4].[C12] = 1) 
       AND ([Join4].[C12] IS NOT NULL)) 
      THEN CAST(NULL AS DECIMAL(19, 4)) 
      WHEN(([Join4].[C11] = 1) 
       AND ([Join4].[C11] IS NOT NULL)) 
      THEN [Join4].[DiscountFactor] 
     END AS [C6], 
     CASE 
      WHEN((NOT(([Join4].[C11] = 1) 
        AND ([Join4].[C11] IS NOT NULL))) 
       AND (NOT(([Join4].[C12] = 1) 
         AND ([Join4].[C12] IS NOT NULL))) 
       AND (NOT(([Join4].[C13] = 1) 
         AND ([Join4].[C13] IS NOT NULL))) 
       AND (NOT(([Join4].[C14] = 1) 
         AND ([Join4].[C14] IS NOT NULL)))) 
      THEN CAST(NULL AS DATETIME2) 
      WHEN(([Join4].[C14] = 1) 
       AND ([Join4].[C14] IS NOT NULL)) 
      THEN CAST(NULL AS DATETIME2) 
      WHEN(([Join4].[C12] = 1) 
       AND ([Join4].[C12] IS NOT NULL)) 
      THEN CAST(NULL AS DATETIME2) 
      WHEN(([Join4].[C11] = 1) 
       AND ([Join4].[C11] IS NOT NULL)) 
      THEN [Join4].[DiscountValidTo] 
     END AS [C7], 
     CASE 
      WHEN((NOT(([Join4].[C11] = 1) 
        AND ([Join4].[C11] IS NOT NULL))) 
       AND (NOT(([Join4].[C12] = 1) 
         AND ([Join4].[C12] IS NOT NULL))) 
       AND (NOT(([Join4].[C13] = 1) 
         AND ([Join4].[C13] IS NOT NULL))) 
       AND (NOT(([Join4].[C14] = 1) 
         AND ([Join4].[C14] IS NOT NULL)))) 
      THEN CAST(NULL AS INT) 
      WHEN(([Join4].[C14] = 1) 
       AND ([Join4].[C14] IS NOT NULL)) 
      THEN CAST(NULL AS INT) 
      WHEN(([Join4].[C12] = 1) 
       AND ([Join4].[C12] IS NOT NULL)) 
      THEN CAST(NULL AS INT) 
      WHEN(([Join4].[C11] = 1) 
       AND ([Join4].[C11] IS NOT NULL)) 
      THEN [Join4].[AppliedDiscountAdjustmentClaim_ID] 
     END AS [C8], 
     CASE 
      WHEN((NOT(([Join4].[C11] = 1) 
        AND ([Join4].[C11] IS NOT NULL))) 
       AND (NOT(([Join4].[C12] = 1) 
         AND ([Join4].[C12] IS NOT NULL))) 
       AND (NOT(([Join4].[C13] = 1) 
         AND ([Join4].[C13] IS NOT NULL))) 
       AND (NOT(([Join4].[C14] = 1) 
         AND ([Join4].[C14] IS NOT NULL)))) 
      THEN CAST(NULL AS INT) 
      WHEN(([Join4].[C14] = 1) 
       AND ([Join4].[C14] IS NOT NULL)) 
      THEN CAST(NULL AS INT) 
      WHEN(([Join4].[C12] = 1) 
       AND ([Join4].[C12] IS NOT NULL)) 
      THEN CAST(NULL AS INT) 
      WHEN(([Join4].[C11] = 1) 
       AND ([Join4].[C11] IS NOT NULL)) 
      THEN [Join4].[ExpiredDiscountAdjustmentClaim_ID] 
     END AS [C9], 
     CASE 
      WHEN((NOT(([Join4].[C11] = 1) 
        AND ([Join4].[C11] IS NOT NULL))) 
       AND (NOT(([Join4].[C12] = 1) 
         AND ([Join4].[C12] IS NOT NULL))) 
       AND (NOT(([Join4].[C13] = 1) 
         AND ([Join4].[C13] IS NOT NULL))) 
       AND (NOT(([Join4].[C14] = 1) 
         AND ([Join4].[C14] IS NOT NULL)))) 
      THEN CAST(NULL AS VARCHAR(1)) 
      WHEN(([Join4].[C14] = 1) 
       AND ([Join4].[C14] IS NOT NULL)) 
      THEN CAST(NULL AS VARCHAR(1)) 
      WHEN(([Join4].[C12] = 1) 
       AND ([Join4].[C12] IS NOT NULL)) 
      THEN CAST(NULL AS VARCHAR(1)) 
      WHEN(([Join4].[C11] = 1) 
       AND ([Join4].[C11] IS NOT NULL)) 
      THEN CAST(NULL AS VARCHAR(1)) 
      ELSE [Join4].[Reference] 
     END AS [C10], 
     CASE 
      WHEN((NOT(([Join4].[C11] = 1) 
        AND ([Join4].[C11] IS NOT NULL))) 
       AND (NOT(([Join4].[C12] = 1) 
         AND ([Join4].[C12] IS NOT NULL))) 
       AND (NOT(([Join4].[C13] = 1) 
         AND ([Join4].[C13] IS NOT NULL))) 
       AND (NOT(([Join4].[C14] = 1) 
         AND ([Join4].[C14] IS NOT NULL)))) 
      THEN CAST(NULL AS DATETIME2) 
      WHEN(([Join4].[C14] = 1) 
       AND ([Join4].[C14] IS NOT NULL)) 
      THEN CAST(NULL AS DATETIME2) 
      WHEN(([Join4].[C12] = 1) 
       AND ([Join4].[C12] IS NOT NULL)) 
      THEN CAST(NULL AS DATETIME2) 
      WHEN(([Join4].[C11] = 1) 
       AND ([Join4].[C11] IS NOT NULL)) 
      THEN CAST(NULL AS DATETIME2) 
      ELSE [Join4].[DueDate] 
     END AS [C11], 
     [Extent7].[Reference] AS [Reference] 
    FROM [dbo].[ClaimBatchLine] AS [Extent1] 
     INNER JOIN 
    (
     SELECT [Extent2].[Id] AS [Id1], 
      [Columns left out for brevity...] 
     FROM [dbo].[Claim] AS [Extent2] 
      LEFT OUTER JOIN 
     (
     SELECT [Extent3].[Id] AS [Id], 
       [Extent3].[CostClaimAnnouncement_ID] AS [CostClaimAnnouncement_ID], 
       [Extent3].[DiscountFactor] AS [DiscountFactor], 
       [Extent3].[DiscountValidTo] AS [DiscountValidTo], 
       [Extent3].[AppliedDiscountAdjustmentClaim_ID] AS [AppliedDiscountAdjustmentClaim_ID], 
       [Extent3].[ExpiredDiscountAdjustmentClaim_ID] AS [ExpiredDiscountAdjustmentClaim_ID], 
       CAST(1 AS BIT) AS [C1] 
     FROM [dbo].[CostClaim] AS [Extent3] 
    ) AS [Project1] ON [Extent2].[Id] = [Project1].[Id] 
      LEFT OUTER JOIN 
     (
     SELECT [Extent4].[Id] AS [Id], 
       [Extent4].[IsAppeared] AS [IsAppeared], 
       CAST(1 AS BIT) AS [C1] 
     FROM [dbo].[InterestClaim] AS [Extent4] 
    ) AS [Project2] ON [Extent2].[Id] = [Project2].[Id] 
      LEFT OUTER JOIN 
     (
     SELECT [Extent5].[Id] AS [Id], 
       [Extent5].[AdjustmentClaimReason_ID] AS [AdjustmentClaimReason_ID], 
       [Extent5].[User_ID] AS [User_ID], 
       CAST(1 AS BIT) AS [C1] 
     FROM [dbo].[AdjustmentClaim] AS [Extent5] 
    ) AS [Project3] ON [Extent2].[Id] = [Project3].[Id] 
      LEFT OUTER JOIN 
     (
     SELECT [Extent6].[Id] AS [Id], 
       [Extent6].[Reference] AS [Reference], 
       [Extent6].[DueDate] AS [DueDate], 
       CAST(1 AS BIT) AS [C1] 
     FROM [dbo].[InvoiceClaim] AS [Extent6] 
    ) AS [Project4] ON [Extent2].[Id] = [Project4].[Id] 
    ) AS [Join4] ON [Extent1].[Claim_ID] = [Join4].[Id1] 
     LEFT OUTER JOIN [dbo].[InvoiceClaim] AS [Extent7] ON [Join4].[Id1] = [Extent7].[Id] 
    WHERE 1 = [Extent1].[ClaimBatch_ID] 
) AS [Project5] 
ORDER BY [Project5].[ID] ASC 
OFFSET 0 ROWS FETCH NEXT 15000 ROWS ONLY; 

地獄はここで何が起こっていますか! LINQpadのスクラッチは、最初はうまく見えました。しかし、プロダクションコードの最終的なクエリは単なる恐ろしいものです! これらのクエリは、いくつかの単純なアプリケーションでうまくいくかもしれません。これは、さらに多くのレコードの中で500kのレコードを照会するときにはうまくありません。私はLINQを使用する代わりに、単純なSQLテーブル値関数を使用します。お気の毒に。

答えて

2

は、この方法を試してみてください:左を実行する方法についての詳細情報については

from cbl in ClaimBatchLines 
join c in Claims on c.ID equals cbl.Claim_ID 
join ic in InvoiceClaims on ic.ID equals c.ID into g 
from e in g.DefaultIfEmpty() 
where cbl.ClaimBatch_ID == 1 
select new {cbl, c, e.Reference} 

は、LINQに参加を見て、このlink

+0

left joinを使用するようになりますが、実際に確認する必要はありませんSQLは自然にそれを処理するため、L2Eクエリでは 'null 'となります。あなたが気にする必要があるのは、プロパティが値型である場合です。その場合、それを対応するnull可能型に昇格(キャスト)する必要があります。 –

+1

入手しました。 info @ IvanStoevに感謝、毎日新しいことを学ぶ毎日 – octavioccl

+1

EF Coreはすべてを完全に変更するので再開する必要があります:) –

1

は、LINQのjoinを使用して、それを書いてみてください。

var q = 
(from cbl in ClaimBatchLines 
join c in Claims on cbl.Claim_ID equals c.ID 
join tmpIc in InvoiceClaims on c.ID equals tmpIc.ID into g 
from ic in g.DefaultIfEmpty() 
where cbl.ClaimBatch_ID == 1 
select new { cbl, c, ic }) 
    .OrderBy(x => x.cbl.ID) 
    .Skip(recordsPerPage * page) 
    .Take(recordsPerPage); 
+0

これは、LEFT JOINの結果として、InvoiceClaimテーブルから列のサブセットを選択するサブクエリとなります。私はそれが受け入れられた答えと大きなパフォーマンスの違いを作るかどうかは分かりません。しかし、ClaimBatchesは大きい(500Kレコード)ことができ、SQLがシンプルになればなるほど、私はより良いと思います。しかし、助けてくれてありがとう。 –

+0

+1注文、スキップ、お引き取りのために完了した場所。私はクエリが()で区切られ、他のLINQメソッドと組み合わせて使用​​されることは知りませんでした。 –

2

最も簡単な方法は、あなたのところの参加に移動することです:

from cbl in ClaimBatchLines where cbl.ClaimBatch_ID == 1 
from c in Claims where c.ID == cbl.Claim_ID 
from ic in InvoiceClaims.Where(x => x.ID == c.ID).DefaultIfEmpty() 
select new {cbl, c, ic.Reference} 

これは、クエリはInvoiceClaimsテーブル

+0

これは、クレームに対して「クロスジョイン」を生成します。 ClaimBatchLineは常にClaimと1対1の関係にあります。私はこの投稿から私が理解しているから:http://stackoverflow.com/questions/17759687/cross-join-vs-inner-join-in-sql-server-2008インナー・ジョインを使用するのは、意図的な2つのテーブル間の関係。しかし、この投稿http://stackoverflow.com/a/6418253/1567665によると、パフォーマンスは同じです。受け入れられた答えではありません。正解の場合+1。 –

関連する問題