はじめに

LINQでの内部結合、外部結合についてのメモです。
間違いや勘違いに関しましてはご指摘いただけると幸いです。

テーブルのイメージ

PersonTable
PersonID
Name
Age
mail
TeamTable
TeamID
TeamName
PersonID

内部結合の方法

内部結合は結合するテーブル同士で、結合が発生する場合のみ取得する結合方法です。

クエリ式

var query =
             from p in PersonTable
             join t in TeamTable
             on p.PersonID equals t.PersonID
         where p.Age > 25
             select new
             {
               PersonID = p.PersonID,
               Name = p.Name,
              TeamName = tj.TeamName
             };

メソッド構文

メソッド構文のJoin句は少し複雑になります。

var query =
             PersonTable
             .Join(
                   TeamTable,
                   t => t.PersonID,
                   p => p.PersonID,
                   (person, team) => new
                   {
                     PersonID = person.PersonID,
                     Name = person.Name,
                     TeamName = team.TeamName
                   }
             )
             .Select(jointable=> new
             {
               PersonID = jointable.PersonID,
               Name = jointable.Name,
               TeamName = jointable.TeamName
             };

メソッド構文のJoin句は、以下のような構成になっています。

.Join(
      結合するテーブル,
      結合する側の結合条件(TeamTable),
      結合される側の結合条件(PersonTable),
      ((結合する側を指す範囲変数), (結合される側を指す範囲変数))
    => new
      {
         (結合後のテーブル)
      })

クエリ式よりも厳密になり、結合後の仮想テーブルを=>以降に定義しています。

外部結合の方法

外部結合はテーブルに結合した際、主体となるテーブルに結合が発生しない行も取得する結合方法です。

厳密にはLINQでは外部結合は存在しません。
intoを使用する、グループ結合というLINQ独自の結合を利用して外部結合のような挙動をさせています。
また、LINQでは左外部結合にあたるものしかありません。

クエリ式

var query =
             from p in PersonTable
             join t in TeamTable
             on p.PersonID equals t.PersonID into tJoin
             from tj in tJoin.DefaultIfEmpty()
             where p.Age > 25
             select new 
             {
               PersonID = p.PersonID,
               Name = p.Name,
               TeamName = String.IsNullOrEmpty(tj.TeamName) ? "該当なし" : tj.TeamName
             };

内部結合と異なる部分は以下です。

 into tJoin from tj in tJoin.DefaultIfEmpty()

まず、

 into tJoin 

にて、結合する側を指す範囲変数を定義します。

 from tj in tJoin.DefaultIfEmpty()

結合する側に「.DefaultIfEmpty()」を付けることで、結合される側(db.PersonTable)の行に、結合する側(db.TeamTable)と結合する要素がない場合、
結合する側(db.TeamTable)の空の内容が生成されます。

LINQ to Entitiesの場合、.DefaultIfEmpty()にて生成される内容を定義することができないので、select句で空の場合の処理を入れる必要があります。

メソッド構文

var query = PersonTable
            .GroupJoin(
                TeamTable,
                t => t.PersonID,
                p => p.PersonID,
                (person, team) => new
                {
                    PersonID = person.PersonID,
                    Name = person.Name,
                    TeamName = team.Any() > 0 ? team.FirstOrDefault().TeamName : "該当なし"
                }
            );

(person, team)の時に、結合する側(team)がIEnumerable型になっています。
そのため重複しない結合の場合、1つに絞る処理が必要となります。

メソッド構文の外部結合でSelectManyを使用する方法

ご指摘いただいた内容を元に、手元の環境に合わせて外部結合のメソッド構文を作成しなおしました。こちらのほうが実行されるSQLもすっきりします。

var query = PersonTable
            .GroupJoin
            (
               TeamTable,
                p => p.PersonID,
                t => t.PersonID,
                (p, ts) => new
                {
                    p = p,
                    ts = ts
                }
            )
            .SelectMany
            (
                x => x.ts.DefaultIfEmpty(),
                (x, t) => new
                {
                    x.p.PersonID,
                    x.p.Name,
                    TeamName = t.TeamName ?? "該当なし"
                }
            );
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.