SQL から LINQ への変換、パート 8 : 左外部結合と右外部結合 (Bill Horst)


ここでは、このシリーズの以前の投稿 (英語) をお読みになっていることを前提としています。

         


結合について投稿した後で、外部結合について、いくつかの質問を受けました。パート 6 でご覧になったように、VB9 では左外部結合や右外部結合がスムーズにサポートされていません。同じような機能を Group Join で実現する方法をご説明しましたが、VB LINQ で左外部結合、右外部結合、および完全外部結合の結果を正確に得る方法を、皆さんにもう少し詳しくお見せしようと思います。確かに、これらのクエリは、比較的単純な概念の割には非常に複雑です。しかし、残念ながら、VB9 にこれらの機能を実装するためのリソースが VB チームにはありませんでした。このサポートが重要とお考えの場合は、将来のリリースにこれらの機能を含めるよう、Visual Studio チームにご提案ください。ご提案はフィードバック ページから提出していただけます。


 


今回の投稿では左外部結合と右外部結合についてご説明します。完全外部結合については来週の投稿でご説明する予定です。


 


前提条件


 


今回のコード例では、最初の投稿で定義したクラスを多少変更して使用します。下にお見せする宣言では、以前は Nothing (Integer -> Integer? など) を設定できなかったメンバに対して、いくつか Null 許容型を宣言しています。


 







Class Customer


    Public CustomerID As Integer?


    Public ContactName As String


    Public Phone As String


    Public Address As String


    Public City As String


    Public State As String


    Public Zip As String


End Class


 


Class Order


    Public OrderID As Integer?


    Public CustomerID As Integer?


    Public Cost As Single?


    Public Phone As String


    Public OrderDate As DateTime?


    Public ShippingZip As String


    Public ItemName As String


End Class


 


 


また、次のように、CustomerTable 変数と OrderTable 変数を宣言して、いくつかの結果例を示しています。


 






 


Dim CustomerTable As Customer() = { _


    New Customer With {.ContactName = "Bill Horst", .CustomerID = 112}, _


    New Customer With {.ContactName = "John Doe", .CustomerID = 354}, _


    New Customer With {.ContactName = "Jane Doe", .CustomerID = 938}}


 


Dim OrderTable As Order() = { _


    New Order With {.OrderDate = #3/25/1982#, .CustomerID = 112}, _


    New Order With {.OrderDate = #3/13/2005#, .CustomerID = 112}, _


    New Order With {.OrderDate = #9/29/2007#, .CustomerID = 938}, _


    New Order With {.OrderDate = #1/31/2008#, .CustomerID = 444}}


 


 


 


左結合


 


SQL の左外部結合は標準的な内部結合に似ていますが、一致する結果が "右のテーブル" にない場合でも、"左のテーブル" に結果を返します。次にその例をお見せします。


 








SQL


SELECT Contact.ContactName, Shipment.OrderDate


FROM CustomerTable Contact


LEFT OUTER JOIN OrderTable Shipment


   ON Contact.CustomerID = Shipment.CustomerID


 


 


CustomerTable OrderTable に上記のデータがあると仮定すると、このクエリでは次のような結果が返されます。


 








Results:


ContactName     OrderDate


Bill Horst      3/25/1982


Bill Horst      3/13/2005


John Doe        NULL


Jane Doe        9/29/2007


 


VB コードでこのクエリを再現するとしたら、まず、CustomerTable OrderTable との間に Group Join を設定します。集約値を計算するのは嫌なので、Group キーワードを使って、CustomerTable の各メンバに、OrderTable の対応するエントリ配列でエントリを作成します。


 








VB


From Contact In CustomerTable _


Group Join Shipment In OrderTable _


  On Contact.CustomerID Equals Shipment.CustomerID _


  Into RightTableResults = Group


 


Results:


{Contact={Customer}, RightTableResults = {Grouping}}


{Contact={Customer}, RightTableResults = {Order[]}}


{Contact={Customer}, RightTableResults = {Grouping}}


 


 


ただし、配列の連続は避けたいので、From 句を追加して、各 Contact を、対応する配列の個々のメンバとクロス結合します。


 








VB


From Contact In CustomerTable _


Group Join Shipment In OrderTable _


  On Contact.CustomerID Equals Shipment.CustomerID _


  Into RightTableResults = Group _


From Shipment In RightTableResults.DefaultIfEmpty


 


Results:


{Contact = {Customer}, RightTableResults = {Grouping}, Shipment = {Order}}


{Contact = {Customer}, RightTableResults = {Grouping}, Shipment = {Order}}


{Contact = {Customer}, RightTableResults = {Order[]}, Shipment = Nothing}


{Contact = {Customer}, RightTableResults = {Grouping}, Shipment = {Order}}


 


 


次に、必要なフィールド、つまり Contact.ContactName Shipment.OrderDate だけを Select します。


 








VB


From Contact In CustomerTable _


Group Join Shipment In OrderTable _


  On Contact.CustomerID Equals Shipment.CustomerID _


  Into RightTableResults = Group _


From Shipment In RightTableResults.DefaultIfEmpty _


Select Contact.ContactName, _


       Shipment.OrderDate


 


 


このコードにはまだ問題があります。Shipment = Nothing のエントリに到達すると、Select ステートメントが NullReferenceException をスローするのです。これをどうにかしなくてはなりません。独自のメソッドを定義するなど、いくつかの方法があるのですが、ここで選んだのは三項演算子呼び出しを使用する方法です。


 








VB


From Contact In CustomerTable _


Group Join Shipment In OrderTable _


  On Contact.CustomerID Equals Shipment.CustomerID _


  Into RightTableResults = Group _


From Shipment In RightTableResults.DefaultIfEmpty _


Select Contact.ContactName, _


       OrderDate = _


         If(Shipment Is Nothing, Nothing, _


            If(Shipment Is Nothing, New Order, Shipment).OrderDate)


 


Results:


{ContactName = "Bill Horst", OrderDate = {DateTime}}


{ContactName = "Bill Horst", OrderDate = {DateTime}}


{ContactName = "John Doe", OrderDate = Nothing}


{ContactName = "Jane Doe", OrderDate = {DateTime}}


 


 


このコードは確かにとても複雑ですが、集約関数を使用せずに左外部結合と同じ結果を得られます。LINQ に習熟すれば、もっと簡単な方法で外部結合クエリを変換する方法が見つかるかもしれません。


 


対応する右外部結合は次のようになります。


 








VB


Dim RightJoin = _


    From Shipment In OrderTable _


    Group Join Contact In CustomerTable _


      On Contact.CustomerID Equals Shipment.CustomerID _


      Into RightTableResults = Group _


    From Contact In RightTableResults.DefaultIfEmpty _


    Select ContactName = _


             If(Contact Is Nothing, _


                Nothing, _


                If(Contact Is Nothing, _


                   New Customer, _


                   Contact).ContactName), _


           Shipment.OrderDate


 


 


このシリーズに追加してしいとお思いのトピックがありましたら、ぜひコメントしてください。次回は完全外部結合の変換についてご説明します。


 


-      VB IDE テスト、Bill Horst


投稿 : 2008 1 31 11:37 AM


VB チームの Web ログhttp://blogs.msdn.com/vbteam/archive/2008/01/31/converting-sql-to-linq-part-8-left-right-outer-join-bill-horst.aspx (英語) より


Comments (0)

Skip to main content