C#でクエリ構文を書いてみる

# 1. はじめに

* C#のLINQでクエリ構文を使いたい



# 2. LINQとは

* 統合言語クエリ (Language Integrated Query)のこと
* C# 3.0から使用でき、情報コレクションに対して簡単な文法で処理できる
* IEnumerableをサポートするオブジェクトで使用できる
* クエリ構文とメソッド構文の2つの書き方がある
  * クエリ構文: SQLのような書き方ができる
  * メソッド構文：ラムダ演算子(=>)で記述できる



# 3. クエリ構文の書き方



## 3.1 where

* ```using System.Linq```を記述する

```csharp:
using System.Linq;

// (省略)

int[] numbers = {5,4,3,2,1};

// foreach (var number in numbers)と同じ意味
var result = from number in numbers
			where number >= 3
			select number;
			
Debug.Print(string.Join(",", result));
```



## 3.2 orderby

* 昇順：orderby, 降順: orderby desending

```csharp:
using System.Linq;

// (省略)

int[] numbers = {5,4,3,2,1};

// &&:AND条件, ||: OR条件
var result = from number in numbers
			where number >= 3 && number <= 5
			orderby number desending
			select number;
			
Debug.Print(result);
```



## 3.3. let

* 複雑な処理の場合、letで値を対比できる

```csharp:
class LetSample1
{
    static void Main()
    {
        string[] strings =
        {
            "A penny saved is a penny earned.",
            "The early bird catches the worm.",
            "The pen is mightier than the sword."
        };

        // Split the sentence into an array of words
        // and select those whose first letter is a vowel.
        var earlyBirdQuery =
            from sentence in strings
            let words = sentence.Split(' ')
            from word in words
            let w = word.ToLower()
            where w[0] == 'a' || w[0] == 'e'
                || w[0] == 'i' || w[0] == 'o'
                || w[0] == 'u'
            select word;

        // Execute the query.
        foreach (var v in earlyBirdQuery)
        {
            Console.WriteLine("\"{0}\" starts with a vowel", v);
        }

        // Keep the console window open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
    "A" starts with a vowel
    "is" starts with a vowel
    "a" starts with a vowel
    "earned." starts with a vowel
    "early" starts with a vowel
    "is" starts with a vowel
*/
```



# 3.4. group by

* グループごとに取得できるが、SQLのように集約はしない

```csharp:
// ProcuctはID,名前,価格を持つカスタムクラス
var result = from p in product
    		group p by p.Price;
	// 複数の場合は group p by new {p.PodoctId, p.Price}

foreach ( var group in result )
{
    // ここはグループごとにループする
    Console.WriteLine(group.Key);
    foreach (var row in group)
    {
        Console.WriteLine($"id={row.ProductId} name={row.ProductName} price={row.Price}");
    }
}
```



## 3.5. innner join

* 内部結合でSQLのようにキーが合致するレコードのみを取得する

```csharp:
// カスタムクラス sales, saleItemsをキーで内部結合する場合
var result = from a in sales
    		join b in saleItems
    		on a.SaleId equals b.SaleId
        	// 複数条件の場合はnewをする
    		// on new {a.XX, b.XX} equals new {a.XX, b.XX}
    		select new
			{
    			a.SaleId,
    			a.CustomerId,
    			b.ProductId,
    			b.SaleCount,
			};

foreach (var val in result)
{
    Console.WriteLine(val);
}
```



## 3.6. left join

* 外部結合でSQLのようにキーで結合してレコードを取得する
* selectした結果はNullを考慮した書き方に変更する

```csharp:
// カスタムクラス sales, saleItemsをキーで外部結合する場合
var result = from a in sales
    		join b in saleItems
    		on a.SaleId equals b.SaleId into bb
    		from b in bb.DefaultIfEmpty()
    		select new
			{
    			a.SaleId,
    			a.CustomerId,
    			ProductId = b?.ProductId ?? -1,
    			SaleCount = b?.SaleCount ?? 0,
			};

foreach (var val in result)
{
    Console.WriteLine(val);
}
```



# 4. 参考文献

https://www.udemy.com/course/lambda02/

https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/let-clause