C# join 指令使用介紹 (GroupJoin)(Left Join, Full Join)

在 C# 中,LINQ 是非常常使用到的,不過相關的介紹好像沒有像其他的那麼多,於是最近打算介紹多一點 LINQ 相關的內容。

在本文中會使用到兩個物件存放資料,物件格式如下:
    
/// <summary>
/// 學生
/// </summary>
/// <param name="No">學號</param>
/// <param name="Name">姓名</param>
public record Student(string No, string Name);

/// <summary>
/// 測驗成績
/// </summary>
/// <param name="No">學號</param>
/// <param name="Score">成績</param>
public record TestScore(string No, int Score);
    

建立測試資料:
    
// 學生清單
Student[] students =
{
    new Student("001", "小明"),
    new Student("002", "大頭"),
    new Student("003", "張三"),
    new Student("004", "李四"),
    new Student("005", "王五"),
};

// 測驗成績清單
TestScore[] testScores =
{
    new TestScore("100", 100),
    new TestScore("003", 90),
    new TestScore("002", 80),
    new TestScore("001", 70),
};
    

可以看到我們有故意留下 004, 005 沒有成績,而成績清單中反向向排序,且有一個學號 100 號的 100 分,但是沒有被包含在學生清單中,方便我們觀察結果。

在下面的示範中會嘗試使用學號做對應,將學生姓名和成績輸出

內部連接 - Inner Join

內部連接連接為兩邊都有資料時才會顯示,資料量是所有連接方式中最少的,使用 Join 關鍵字。

Join 共有五個參數,只是因為是「擴充方法(Extension Methods)」,所以第一個參數會在 Join 關鍵字的左側。

參數:
  1. 第一個清單
  2. 第二個清單
  3. 第一個清單中用來和第二個清單中內容對應的成員
  4. 第二個清單中用來和第一個清單中內容對應的成員
  5. 回傳的結果(在兩個清單中都有的內容)
    
var list = students.Join(
    testScores,
    student => student.No,
    testScore => testScore.No,
    (student, testScore) => new { student.Name, testScore.Score }
).ToList();
    

上方的 new { student.Name, testScore.Score } 是匿名型別,不用事先明確定義類型,可以直接產生唯獨內容的物件,方便我們快速取得資料。

執行結果:
    
list.ForEach(item => Console.WriteLine($"姓名:{item.Name}, 成績:{item.Score}"));

/*
姓名:小明, 成績:70
姓名:大頭, 成績:80
姓名:張三, 成績:90
*/
    

使用第一個清單順序,且只有兩者相符的內容才有被輸出。

左外部連接 - Left Join

Left Join 是我們在資料庫中常用的方法,在 LINQ 中叫做 GroupJoin 。差別在於第一個清單中的內容就算不在第二個清單中也會被輸出,只是因為沒有在第二個清單中也會被輸出,所以第二個清單中的物件可能會是 NULL,在使用時可以利用 FirstOrDefault() 擴充方法來將找不到時的物件變為預設(NULL),再使用 ?? 運算子安全的將空物件內容賦值即可
    
var list = students.GroupJoin(
    testScores,
    student => student.No,
    testScore => testScore.No,
    (student, testScore) => new { student.Name, Score = testScore.FirstOrDefault()?.Score ?? 0 }
).ToList();
    

執行結果:
    
list.ForEach(item => Console.WriteLine($"姓名:{item.Name}, 成績:{item.Score}"));

/*
姓名:小明, 成績:70
姓名:大頭, 成績:80
姓名:張三, 成績:90
姓名:李四, 成績:0
姓名:王五, 成績:0

*/
    

依然使用第一個清單順序,且就算沒有被包含在清單二中的內容也會被輸出。

全部外部連接 - Full Join

FULL JOIN 一樣是在資料庫中常用的關鍵字,是指只要清單一或清單二有就輸出,不過很可惜的是在 C# 中並沒有像是 Full Join 的方法可以使用,如果需要的話可以參考 Stack Overflow 中的 LINQ - Full Outer Join

延伸閱讀: C# LINQ 從 0 到 1 基礎教學

參考資料:
Microsoft.Learn - Extension Methods (C# Programming Guide)
Microsoft.Learn - Anonymous types
Microsoft.Learn - ?? and ??= operators (C# reference)
Microsoft.Learn - Enumerable.Join Method
Microsoft.Learn - Enumerable.GroupJoin Method

留言