【C#/LINQ】GroupJoin()を使って外部結合(outer join)を行う

投稿者: | 2012年5月9日

LINQのGroupJoin()メソッドを使って、SQLの外部結合(outer join)に相当するデータ操作を行ってみよう。以下のようなテーブルデータがあるとして、それをLINQで結合してみることにする。

Persons

Id Name Age JobId
1 John 56 1
2 Mike 23 NULL
3 Ken 64 2
4 Alice 41 4
5 Tom 22 NULL
Jobs

Id Name
1 Programmer
2 Engineer
3 Sportman
4 King
5 Slave

前回の内部結合(inner join)の例と違うのは、PersonsテーブルのJobIdにNULLの場合があることである。
まず、この例を Persons.JobId と Jobs.Id で内部結合した場合について示す。結果は以下のようになる。


PersonId:1      Name:John       Age:56  Job:Programmer
PersonId:3      Name:Ken        Age:64  Job:Engineer
PersonId:4      Name:Alice      Age:41  Job:King

このように、内部結合(inner join)では結合条件に合致しないデータは結果から除かれる。
これに対し、以下のように左側のデータを残しつつ結合した結果を得たいときは外部結合(outer join)を使う。


PersonId:1      Name:John       Age:56  Job:Programmer
PersonId:2      Name:Mike       Age:23  Job:Null
PersonId:3      Name:Ken        Age:64  Job:Engineer
PersonId:4      Name:Alice      Age:41  Job:King
PersonId:5      Name:Tom        Age:22  Job:Null

それでは、プログラムを見ていこう。

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LinqJoinLessonOuter
{
    class Program
    {
        class Person
        {
            public int Id { get; set; }
            public String Name { get; set; }
            public int Age { get; set; }
            public int? JobId { get; set; }
        }

        class Job
        {
            public int Id { get; set; }
            public String Name { get; set; }
        }

        static void Main(string[] args)
        {
            var persons = new Person[]{
                new Person{ Id = 1,Name = "John",  Age = 56, JobId = 1},
                new Person{ Id = 2,Name = "Mike",  Age = 23, JobId = null},
                new Person{ Id = 3,Name = "Ken",   Age = 64, JobId = 2},
                new Person{ Id = 4,Name = "Alice", Age = 41, JobId = 4},
                new Person{ Id = 5,Name = "Tom",   Age = 22, JobId = null},
            };

            var jobs = new Job[] { 
                new Job{ Id = 1, Name = "Programmer" },
                new Job{ Id = 2, Name = "Engineer" },
                new Job{ Id = 3, Name = "Sportman" },
                new Job{ Id = 4, Name = "King" },
                new Job{ Id = 5, Name = "Slave" }
            };

            // 外部結合を行うメソッド式
            var outerJoin =
                persons.GroupJoin(jobs, p => p.JobId, j => j.Id, (p, j) => new
                {
                    PersonId = p.Id,
                    Name = p.Name,
                    Age = p.Age,
                    Jobs = j.DefaultIfEmpty()
                })
                .SelectMany(x => x.Jobs, (x, j) => new
                {
                    PersonId = x.PersonId,
                    Name = x.Name,
                    Age = x.Age,
                    JobName = j != null ? j.Name : "<unemployed>"
                });

            foreach (var j in outerJoin)
            {
                Console.WriteLine(
                   "PersonId:{0}\tName:{1}\tAge:{2}\tJob:{3}", j.PersonId, j.Name, j.Age, j.JobName);
            }
        }
    }
}

PersonsクラスのJobIdは、nullを使うためnull許容型とした。
プログラムの実行結果は以下のようになった。


PersonId:1      Name:John       Age:56  Job:Programmer
PersonId:2      Name:Mike       Age:23  Job:<unemployed>
PersonId:3      Name:Ken        Age:64  Job:Engineer
PersonId:4      Name:Alice      Age:41  Job:King
PersonId:5      Name:Tom        Age:22  Job:<unemployed>
続行するには何かキーを押してください . . .

参考に、以上の操作をクエリ式で書いた場合とSQLで書いた場合について示す。

クエリ式

var outerJoin =
    from p in persons
    join j in jobs on p.JobId equals j.Id into gs
    from g in gs.DefaultIfEmpty()
    select new { PersonId = p.Id, Name = p.Name, Age = p.Age, JobName = g != null ? g.Name : "<unemployed>" };
SQL

SELECT
    p.Id AS PersonID
    p.Name AS Name
    p.Age AS Age
    j.Name As JobName
FROM
    Persons AS p
LEFT OUTER JOIN
    Jobs AS j
ON p.JobId = j.Id

【C#/LINQ】GroupJoin()を使って外部結合(outer join)を行う」への2件のフィードバック

  1. ピンバック: 【C#/LINQ】Join()を使って内部結合(inner join)を行う – ザワプロ!

へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です