LINQ to SQL左外部結合


140

このクエリはLEFT OUTER結合と同等ですか?

//assuming that I have a parameter named 'invoiceId' of type int
from c in SupportCases
let invoice = c.Invoices.FirstOrDefault(i=> i.Id == invoiceId)
where (invoiceId == 0 || invoice != null)    
select new 
{
      Id = c.Id
      , InvoiceId = invoice == null ? 0 : invoice.Id
}

回答:


167

完全ではありません-left-outer-joinの各「左」行は(2番目のテーブルの)0-n「右」行に一致するため、自分の行は0-1にのみ一致します。左外部結合を行うには、次のものが必要SelectManyDefaultIfEmpty例えば、:

var query = from c in db.Customers
            join o in db.Orders
               on c.CustomerID equals o.CustomerID into sr
            from x in sr.DefaultIfEmpty()
            select new {
               CustomerID = c.CustomerID, ContactName = c.ContactName,
               OrderID = x == null ? -1 : x.OrderID };   

または拡張メソッドを介して


19
誰かがこのクレイジーな構文がどのように機能するか説明できますか?私はそれらのキーワードのどれが魔法のようにそれを左結合にするかを見ることはできません。「into sr」は何をしますか?Linqは時々私を苛立たせます:)
ジョー・フィリップス

2
@JoePhillips私はSQLの経験は豊富ですが、LINQを習得しようとすることは泥の中を歩くようなものです。私はそれが絶対にクレイジーであることに同意します。
Nick.McDermaid 14年

MARC-gravell @:あなたはLINQ変換に私のSQLクエリを解決するために私を助けてもらえ:stackoverflow.com/questions/28367941/...
ヴィシャルIパティル

@VishalIPatil なぜ SQLからLINQに変換するのですか?SQLは問題なく動作し、はるかに予測可能で効率的です...
Marc Gravell

1
@VishalIPatilそう...なぜそれをするのですか?ほぼすべてのLINQツールには、手書きのSQLを実行する機能が含まれています。なぜそれをしないのですか?
Marc Gravell

216

intoステートメントは必要ありません。

var query = 
    from customer in dc.Customers
    from order in dc.Orders
         .Where(o => customer.CustomerId == o.CustomerId)
         .DefaultIfEmpty()
    select new { Customer = customer, Order = order } 
    //Order will be null if the left join is null

そして、はい、上のクエリは確かにLEFT OUTER結合を作成します。

複数の左結合を処理する同様の質問へのリンク: LinqからSQL:複数の左外部結合


14
@Marc Gravvelの答えが機能することはわかっていますが、IMOは左結合がどのように見えるべきかにより一致しているように感じるので、私はこの方法を実際に好みます。
llaughlin 2012

1
すばらしい答えです。5時間を超えるGoogle検索を探しています。これは、SQLが結合を残す唯一の方法です。
ファイサルMq

1
どうもありがとうございました...私はこの午後の解決策を探していましたが、あなたのコードはそれを釘付けにしました(そしてブートするのは自然な感じです)。これを数回賛成できるといいのですが。
ジム

2
@ジムありがとう:-)開発者がこの答えからまだマイルを得ていることをうれしく思います。DefaultIfEmpty()は、intoステートメントを使用するよりもはるかに自然に感じることに完全に同意します。
アミール

7
私と同じようにこれを見つけた人のためのメモです。これにより、CROSS APPLY 内に LEFT OUTER JOIN が発生します。つまり、結合の右側に複数の一致がある場合、重複が発生します。Marc Gravellのソリューションは、「かなり」ではありませんが、探していた適切なSQL出力と結果セットを提供してくれました。
Mike U

13
Public Sub LinqToSqlJoin07()
Dim q = From e In db.Employees _
        Group Join o In db.Orders On e Equals o.Employee Into ords = Group _
        From o In ords.DefaultIfEmpty _
        Select New With {e.FirstName, e.LastName, .Order = o}

ObjectDumper.Write(q) End Sub

http://msdn.microsoft.com/en-us/vbasic/bb737929.aspxを確認してください


うまくいきましたが、OPがc#を使用しているようです。VB構文は奇妙に異なります。
Levitikon

5

1つの解決策が見つかりました。この種類のSQL(左結合)をLinqエンティティに変換する場合...

SQL:

SELECT * FROM [JOBBOOKING] AS [t0]
LEFT OUTER JOIN [REFTABLE] AS [t1] ON ([t0].[trxtype] = [t1].[code])
                                  AND ([t1]. [reftype] = "TRX")

LINQ:

from job in JOBBOOKINGs
join r in (from r1 in REFTABLEs where r1.Reftype=="TRX" select r1) 
          on job.Trxtype equals r.Code into join1
from j in join1.DefaultIfEmpty()
select new
{
   //cols...
}

このコメントを参照してください。Linq-to-SQLエンティティはサポートしていませんDefaultIfEmpty
TJクラウダー

2

もう1つ追加したいのですが。LINQ to SQLでは、DBが適切に構築されていて、テーブルが外部キー制約を介して関連付けられている場合、結合を行う必要はありません。

LINQPadを使用して、次のLINQクエリを作成しました。

//Querying from both the CustomerInfo table and OrderInfo table
from cust in CustomerInfo
where cust.CustomerID == 123456
select new {cust, cust.OrderInfo}

これは以下の(少し切り捨てられた)クエリに翻訳されました

 -- Region Parameters
 DECLARE @p0 Int = 123456
-- EndRegion
SELECT [t0].[CustomerID], [t0].[AlternateCustomerID],  [t1].[OrderID], [t1].[OnlineOrderID], (
    SELECT COUNT(*)
    FROM [OrderInfo] AS [t2]
    WHERE [t2].[CustomerID] = [t0].[CustomerID]
    ) AS [value]
FROM [CustomerInfo] AS [t0]
LEFT OUTER JOIN [OrderInfo] AS [t1] ON [t1].[CustomerID] = [t0].[CustomerID]
WHERE [t0].[CustomerID] = @p0
ORDER BY [t0].[CustomerID], [t1].[OrderID]

LEFT OUTER JOIN上記に注意してください。


1

パフォーマンスに注意する:

少なくともEF Coreでそれを経験しましたでは、ここで与えられた異なる回答が異なるパフォーマンスをもたらす可能性があること。OPがLinq to SQLについて質問したことは承知していますが、EF Coreでも同じ質問が発生するようです。

私が処理しなければならなかった特定のケースでは、Marc Gravellによる(構文的に優れた)提案により、クロス適用内に左結合が発生しました-Mike Uが説明したのと同様に- この結果、この特定のクエリの推定コストは2でしたクロス結合のないクエリと比較して倍の時間。サーバーの実行時間は3倍の差がありました。[1]

Marc Gravellによるソリューションは、クロス結合のないクエリをもたらしました。

コンテキスト:基本的に、2つのテーブルで2つの左結合を実行する必要があり、それぞれのテーブルで別のテーブルへの結合が必要でした。さらに、左結合を適用する必要があるテーブルで他のwhere条件を指定する必要がありました。さらに、メインテーブルに2つの内部結合がありました。

推定オペレーターコスト:

  • クロス適用:0.2534
  • クロス適用なし:0.0991。

サーバーの実行時間(ミリ秒)(クエリは10回実行され、SET STATISTICS TIME ONを使用して測定されます):

  • クロス適用:5、6、6、6、6、6、6、6、6、6、6
  • クロス適用なし:2、2、2、2、2、2、2、2、2、2、2

(最初の実行は両方のクエリで遅くなりました。何かがキャッシュされているようです。)

テーブルサイズ:

  • メインテーブル:87行、
  • 左結合の最初のテーブル:179行。
  • 左結合の2番目のテーブル:7行。

EF Coreバージョン:2.2.1。

SQL Serverのバージョン:MS SQL Server 2017-14 ...(Windows 10)。

関連するすべてのテーブルには、主キーのみのインデックスがありました。

私の結論:生成されたSQLは実際には異なる可能性があるため、常に確認することをお勧めします。


[1]興味深いことに、MS SQL Server Management Studioで「クライアント統計」を設定すると、反対の傾向が見られました。つまり、クロス適用なしのソリューションの最後の実行に1秒以上かかりました。私はここで何かがうまくいかなかったと思います-多分私のセットアップで。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.