LINQ 語法應用
前言
使用 Customers 和 Orders 兩個表,逐步了解幾個 LINQ 的實際語法應用,包含:Join (連接)、Grouping (分組)、Ordering (排序) 和 Aggregates (聚合)。
示範資料表
Customers (客戶) 表
| Id | Name | Country |
|---|---|---|
| 1 | Allen | Taiwan |
| 2 | Betty | USA |
| 3 | Carol | Taiwan |
| 4 | David | UK |
Orders (訂單) 表
| Id | CustomerId | Country | ProductName | OrderDate | Amount |
|---|---|---|---|---|---|
| 101 | 1 | Taiwan | Laptop | 2024/01/15 | 30000 |
| 102 | 1 | Taiwan | Mouse | 2024/09/16 | 1200 |
| 103 | 2 | USA | Keyboard | 2024/01/20 | 2500 |
| 104 | 3 | Taiwan | Laptop | 2024/09/25 | 30000 |
| 105 | 3 | Taiwan | Monitor | 2024/10/26 | 8000 |
| 106 | 5 | USA | Speaker | 2024/02/01 | 3500 |
1. 內部連接 (Inner Join)
在 LINQ 中,join 關鍵字用來執行內部連接,join 只會回傳兩個集合中都有符合條件的資料。在這個例子中,只會選取在 Customers 和 Orders 表中都能找到對應 CustomerId 的資料。
LINQ 查詢語法
1 | var innerJoinResult = from c in customers |
這段程式碼等同於以下的 SQL 語法:
1 | SELECT c.Name, o.ProductName, o.Amount |
查詢結果
David (Id=4) 和 Speaker (CustomerId=5) 的資料都不會出現在結果中,因為 David 沒有下單,而 Speaker 的 CustomerId 在 Customers 表中找不到對應資料。
| CustomerName | ProductName | Amount |
|---|---|---|
| Allen | Laptop | 30000 |
| Allen | Mouse | 1200 |
| Betty | Keyboard | 2500 |
| Carol | Laptop | 30000 |
| Carol | Monitor | 8000 |
2. 左連接 (Left Join)
左連接會回傳左邊集合 (Customers) 的所有資料,即使在右邊集合 (Orders) 中沒有找到符合的項目。如果沒有匹配的資料,右邊的欄位會顯示為 null。
LINQ 沒有直接的關鍵字來做左連接,但你可以透過 into + DefaultIfEmpty() 來模擬這個行為。如果右邊集合找不到相符的元素,則使用 DefaultIfEmpty() 產生一個空集合。
LINQ 查詢語法
1 | var leftJoinResult = from c in customers |
在 from order in customerOrders.DefaultIfEmpty() 這個步驟中,如果 customerOrders 這個分組裡有資料,就會取出實際的 order 物件,如果沒有資料,DefaultIfEmpty() 會回傳一個包含 null 的集合,確保 order 在沒有匹配訂單時會是 null,而不會產生錯誤。
查詢結果
David 的資料被保留下來了,且他的 ProductName 和 Amount 都是空值 (因為沒有下單)。
| CustomerName | ProductName | Amount |
|---|---|---|
| Allen | Laptop | 30000 |
| Allen | Mouse | 1200 |
| Betty | Keyboard | 2500 |
| Carol | Laptop | 30000 |
| Carol | Monitor | 8000 |
| David | 0 |
3. 分組 (Grouping)
接下來我們來看看如何對 Orders 表進行分組,並對分組後的資料進行聚合運算。
依單一屬性分組 (Group by Single Property)
我們想計算每個客戶總共下了多少訂單金額。
LINQ 查詢語法
1 | var groupedByCustomer = from order in orders |
這段程式碼等同於以下的 SQL 語法:
1 | SELECT CustomerId, SUM(Amount) AS TotalAmount, Count(*) |
查詢結果
| CustomerId | TotalAmount | OrderCount |
|---|---|---|
| 1 | 31200 | 2 |
| 2 | 2500 | 1 |
| 3 | 38000 | 2 |
| 5 | 3500 | 1 |
依多個屬性分組 (Group by Multiple Properties)
假設我們想計算每個國家中,每個客戶所下的訂單總額。
LINQ 查詢語法
1 | var groupedByCountryAndCustomer = from order in orders |
這段程式碼等同於以下的 SQL 語法:
1 | SELECT Country, CustomerId, SUM(Amount) AS TotalAmount |
查詢結果
| Country | CustomerId | TotalAmount |
|---|---|---|
| Taiwan | 1 | 31200 |
| USA | 2 | 2500 |
| Taiwan | 3 | 38000 |
| USA | 5 | 3500 |
4. 分組後排序 (Ordering Groups)
你可以根據分組後的一個或多個屬性來進行排序。這在 SQL 語法上是直接在 GROUP BY 後接 ORDER BY,而在 LINQ 查詢語法中則是在 group by 後接 orderby。
LINQ 查詢語法
1 | var groupedData = from order in orders |
這段程式碼等同於以下的 SQL 語法:
1 | SELECT Country, CustomerId, SUM(Amount) AS TotalAmount |
LINQ 方法語法 (Method Syntax):
1 | var groupedData = orders |
5. 條件式聚合運算 (Conditional Aggregates)
在 SQL 中,我們經常使用 CASE WHEN 語法在聚合運算中加入條件判斷。
範例一
在 LINQ 中,我們可以使用 Where() 來實現類似 SQL 中 CASE WHEN 的條件判斷,這樣可以針對不同條件進行聚合計算。
LINQ 查詢語法
假設我們想計算每個客戶訂單中,金額大於 10000 的總額,以及金額小於等於 10000 的總額。
1 | var conditionalAggregate = from order in orders |
這段程式碼等同於以下的 SQL 語法:
1 | SELECT CustomerId, |
查詢結果
| CustomerId | HighValueOrdersTotal | LowValueOrdersTotal |
|---|---|---|
| 1 | 30000 | 1200 |
| 2 | 0 | 2500 |
| 3 | 30000 | 8000 |
| 5 | 0 | 3500 |
範例二
也可以使用三元運算子 ? : 來實現類似 SQL 中 CASE WHEN 的條件判斷。
LINQ 查詢語法
假設我們想計算每個客戶訂單中,6/30以前 的總額,以及7/1以後 的總額。
1 | var conditionalAggregate = from order in orders |
這段程式碼等同於以下的 SQL 語法:
1 | SELECT CustomerId, |
查詢結果
| CustomerId | Before0630 | After0701 |
|---|---|---|
| 1 | 30000 | 1200 |
| 2 | 2500 | 0 |
| 3 | 0 | 38000 |
| 5 | 3500 | 0 |