SQLの基本構文

# リレーショナルデータベースマネジメントシステム（RDBMS）
dataを表形式で管理→SQLでデータの登録・検索・更新・削除
+ Oracle Datebase
+ MySQL
+ Postgresql
+ SQLite

# データベースの種類
+ リレーショナルデータベース： 
+ 階層型データベース
+ ネットワーク型データベース

# テーブル
**テーブル**：ある一定のまとまりのある、表形式のデータ群
**カラム**：縦方向、列のデータこと
**レコード（ロウ）**：横方向、行のデータこと
**フィールド**：レコードを構成する１つ１つの要素。Excelでいうセル

カラム・レコード・フィールドの関係性は、複数のレコードの同じフィールドを集めたものがカラム
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727339/7ba9ffe3-38e9-6891-fe71-ffcd71f63556.png)

 
# ＳＱＬ構文
## SELECT
何のデータをどのテーブルから取り出すのか？を考えると良い。

```
SELECT　カラム名　FROM テーブル名;
```
+ データを複数抽出
SELECT　ID, user, post　FROM　post_table　のように『，区切り』で複数抽出できる
+ カラム名を変更する
SELECT　ID, user as ユーザー名　FROM　post_table　のように『as』を使用すると変更して抽出できる
+ 全てのデータを抽出
SELECT　*　FROM　post_table　のようにカラム名を『*』に変更すると全てのデータを抽出することができる

# WHERE
**特定の条件でデータを抽出**する場合に使用

```
SELECT　カラム名　FROM テーブル名　WHERE 条件式;
```
列指定はカラム名、行指定は条件式と覚えると良い
条件式には、イコールや大なり、小なり、否定などの式が入る

```SELECT　*　FROM　post_table　WHERE　user = taro;```
上記SQL構文は、post_tableからuserがtaroのデータを全ての列で取得するとなる。

## ORDER　BY
**抽出対象のデータの並び替え（昇順、降順)をするため**に使用できる。
```
SELECT　カラム名　FROM テーブル名　ORDER BY　並べ替え条件式;
```
```SELECT　*　FROM　post_table　ORDER BY　created_at ASC;```
上記SQL構文は、post_tableからすべてのカラムをcreated_atの昇順に取得するとなる。

:::note info
昇順は、**ASC**（ASCはORDER BYのデフォルトが昇順なので省略できる）
降順は、**DESC**
:::

+ 複数の条件式を指定できる
```SELECT　*　FROM　post_table　ORDER BY　created_at, user_id DESC;```　
のように『，区切り』で指定できるが、先に書いたカラム名（ここではcreated_at）が優先される

+ WHEREとの組み合わせ
```SELECT　*　FROM　post_table　 WHERE　user = taro　ORDER BY　created_at, user_id DESC;```
のようにWHEREと組み合わせることも可能。

:::note warn
構文の書き方で注意してほしいのは、ORDER BYは、FROMとWHEREのあとに書く。
ORDER BYは最後に書くと覚える。
:::

## GROUP BY
**データをルールにしたがってグルーピング**するもの。
SELECTにGROUP BYを付けることで、グルーピングしたいカラムをもとに、集約関数を使って合計や平均、最小値や最大値、カウントなどが計算できるようになる。

```
SELECT　
   グルーピングをするカラム,集約関数（集計対象カラム）
FROM 
   テーブル名　
WHERE　
   条件式　
GROUP BY　
   グルーピングをするカラム
ORDER BY　
   並べ替え条件式;
```
```SELECT 商品分類,sum(売上金額)　FROM product_table 　GROUP BY 売上金額;```
product_tableから商品分類ごとに売上金額を合計したデータを商品分類と一緒に抽出する。という意味になる。

+ ORDER BYとの組み合わせ
``SELECT 商品名,sum(売上金額)　FROM product_table
GROUP BY 商品名
ORDER BY sum(売上金額);``
product_tableから商品分類ごとに売上金額を合計したデータを商品分類の昇順で抽出する。となる。

+ 他の主な集計関数
``SELECT 商品, avg(売上金額), min(売上金額), max(売上金額)　FROM product_table　GROUP BY 商品名;``
**avgは平均**、**maxは最大値**、**minは最小値**を出力する。
上記のように集計関数は複数あってもOK。

``SELECT 商品, count (売上金額)　FROM product_table　GROUP BY 商品名;``
グルーピングされたレコードの行数を取得する集約関数。
このSQLを実行すると、商品名の同じレコードがいくつあるのかを出力される。

また、count関数はテーブル内にいくつのレコードがあるのかを出力する場合もよく使用される。
``SELECT count (*)　FROM product_table;``
カラム名を指定せずに*を指定すると、テーブルの全件データ数を取得できる。
テーブルの全件数を知りたい場合に頻繁に使用する。

## HAVING
グルーピングされたデータの中から、さらに**特定の条件でデータ抽出**したいときに使用する。

```
SELECT　
   グルーピングをするカラム,集約関数（集計対象カラム）
FROM 
   テーブル名
WHERE　
   条件式
GROUP BY　
   グルーピングをするカラム
HAVING 
   集計関数（集計対象カラム）で条件式
ORDER BY　
   並べ替え条件式;
```

``SELECT 商品名,sum(売上金額)
FROM product_table
GROUP BY 商品名
HAVING sum(売上金額) >= 100000;``

上記SQLを実行すると、売上金額が10万円以上の商品名のみ抽出することができる。
HAVINGに集約関数を含んだカラムを直接条件式に入れ込むことで、HAVINGを元にしたデータ抽出できる。

+ WHEREとの違い
WHEREが単純にテーブル内のデータを抽出。
HAVINGは**グルーピングしたデータから**さらに抽出条件を付けられる。
しかし、WHEREもHAVINGもやっていることは同じで、どちらもデータに対して抽出処理を実行される。
異なるのは呼ばれるタイミングのみ。

:::note info
【実行順番】
**FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY** で、
『どのテーブルの（FROM）、どのデータを(WHERE)、どうグルーピングし(GROUP BY)、グルーピングしたデータをどう抽出し(HAVING)、どのカラム を(SELECT)、どう表示するか（ORDER BY）」の順番で実行。
:::

**WHEREはGROUP BYよりも前に、HAVINGはGROUP BYの後に実行される。**
HAVINGは、GROUP BYされた後のデータに抽出条件を付与するため、**集計関数を使用できる**。
WHEREは、GROUP BYされる前にデータに抽出条件を付与するため、集計関数を使用できない。

以上を踏まえ、下記のSQLを実行すると、
``SELECT
    商品名
    , sum(売上金額)
FROM
    product_table
WHERE
    商品名 !='パンツ'
GROUP BY
    商品名
HAVING
    sum(売上金額) <= 100000;``

パンツ以外の売上金額の合計を集計し、売上金額が10万円以下のみのデータを抽出される。

## JOIN
**テーブルとテーブルを結合**する

**JOINの種類**
+ 内部結合
**inner join**
+ 外部結合
**left join**
**right join**
**outer join**

内部結合では、**on句で指定した条件に一致するレコードのみ**SQLの実行結果に表示
外部結合では**on句で指定した条件に一致しないレコードも実行結果に表示**され、FROMを指定したテーブルのレコードをすべて抽出される。

### INNER JOIN

```
SELECT　カラム名
FROM　テーブル名Ａ（テーブル名 as Aの意味）
INNER JOIN　テーブル名B（テーブル名 as Bの意味）　→ FROMとWHEREの間に記述
ON　A.キー ＝ B.キー　→ 「どのカラムで結合するか」を指定
WHERE　条件式;
```
例）
テーブル１：testA
| 氏名 | 出席番号 | 数学 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 50 |
| 佐藤 | 2 | 60 |
| 高橋 | 3 | 40 |
| 田中 | 4 | 80 |

テーブル２：testB
| 氏名 | 出席番号 | 国語 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 70 |
| 佐藤 | 2 | 60 |
| 伊藤 | 5 | 80 |
| 渡邊 | 6 | 50 |

```
SELECT　
   A.氏名, A.数学, B.国語
FROM　
   testA as A　→ 別名をつけている
INNER JOIN　
   testB as B → 別名をつけている
ON　
   A.氏名 ＝ B.氏名
```
上記のSQLを実行すると
| 氏名 | 出席番号 | 数学 | 国語 |
| :-: | :-: | :-: | :-: |
| 鈴木 |	1 |	50 |	70 |
| 佐藤 |	2 |	60 |	60 |

ベン図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727339/707541e1-57a6-f096-fefe-c72c5e4ba15e.png)

SELECTのA.氏名は「testAテーブルの氏名のカラム」
A.数学は「testAテーブルの数学のカラム」
B.国語は、「testBテーブルの国語のカラム」からデータを取得し、両方のテーブルからデータを同時に取得できるようになる。
ONは、どのカラムで結合するかを指定するので、
A.氏名でtestAテーブルのカラムの氏名、B.氏名でtestBテーブルのカラムの氏名をキーにして結合
それぞれのテーブルの氏名では、鈴木と佐藤が共通するレコードなので、この2つのレコードが抽出される。

### LEFT JOIN

```
SELECT　カラム名
FROM　テーブル名Ａ（テーブル名 as Aの意味）
LEFT JOIN　テーブル名B（テーブル名 as Bの意味）　→ FROMとWHEREの間に記述
ON　A.キー ＝ B.キー　→ 「どのカラムで結合するか」を指定
WHERE　条件式;
```
例）
テーブル１：testA
| 氏名 | 出席番号 | 数学 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 50 |
| 佐藤 | 2 | 60 |
| 高橋 | 3 | 40 |
| 田中 | 4 | 80 |

テーブル２：testB
| 氏名 | 出席番号 | 国語 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 70 |
| 佐藤 | 2 | 60 |
| 伊藤 | 5 | 80 |
| 渡邊 | 6 | 50 |
```
SELECT　*  → すべてのカラムを指定
FROM　testA A　→ 別名をつけている
LEFT JOIN　testB B → 別名をつけている
ON　A.氏名 ＝ B.氏名　→ 両方のテーブルに存在する氏名のカラムを指定
```
上記のSQLを実行すると
| 氏名 | 出席番号 | 数学 | 氏名 | 出席番号 | 国語 |
| :-: | :-: | :-: | :-: | :-: | :-: |
| 鈴木 | 1 | 50 | 鈴木 | 1 | 70 |
| 佐藤 | 2 | 60 | 佐藤 | 2 | 60|
| 高橋 | 3 | 40 | NULL | NULL | NULL |
| 田中 | 4 | 80 | NULL | NULL | NULL |

ベン図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727339/a2a52b29-d1e7-4094-c5a5-c4a976da6f1f.png)

 
このようにLEFT JOINでは、testAのテーブル(FROMで指定したテーブル)を基準として、もう１つのテーブルを結合させる。
on句で指定した氏名で、キーとして結合できなかったレコードは**NULL**として補完される。

### RIGHT JOIN（外部結合）

```
SELECT　カラム名
FROM　テーブル名Ａ（テーブル名 as Aの意味）
RIGHT JOIN　テーブル名B（テーブル名 as Bの意味）　→ FROMとWHEREの間に記述
ON　A.キー ＝ B.キー　→ 「どのカラムで結合するか」を指定
WHERE　条件式;
```

例）
テーブル１：testA
| 氏名 | 出席番号 | 数学 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 50 |
| 佐藤 | 2 | 60 |
| 高橋 | 3 | 40 |
| 田中 | 4 | 80 |

テーブル２：testB
| 氏名 | 出席番号 | 国語 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 70 |
| 佐藤 | 2 | 60 |
| 伊藤 | 5 | 80 |
| 渡邊 | 6 | 50 |
```
SELECT　*  → すべてのカラムを指定
FROM　testA A　→ 別名をつけている
RIGHT JOIN　testB B → 別名をつけている
ON　A.氏名 ＝ B.氏名　→ 両方のテーブルに存在する氏名のカラムを指定
```

上記のSQLを実行すると
| 氏名 | 出席番号 | 数学 | 氏名 | 出席番号 | 国語 |
| :-: | :-: | :-: | :-: | :-: | :-: |
| 鈴木 | 1 | 50 | 鈴木 | 1 | 70 |
| 佐藤 | 2 | 60 | 佐藤 | 2 | 60|
| NULL | NULL | NULL | 伊藤 | 5 | 80 |
| NULL | NULL | NULL | 渡辺 | 6 | 50 |

ベン図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727339/dda50699-bf4d-79d0-cd79-6808c405010c.png)

このようにRIGHT JOINでは、testBのテーブル(RIGHT JOINで指定したテーブル)を基準として、もう１つのテーブルを結合させる。
on句で指定した氏名で、キーとして結合できなかったレコードは**NULL**として補完される。

:::note info
LEFT JOIN、RIGHT JOINは相互に書き換え可能なため、自分が使いやすい方を使用して構わない。
混在させてはいけない。
:::

### FULL OUTER JOIN（OUTER JOIN）

```
SELECT　カラム名
FROM　テーブル名Ａ（テーブル名 as Aの意味）
OUTER JOIN　テーブル名B（テーブル名 as Bの意味）　→ FROMとWHEREの間に記述
ON　A.キー ＝ B.キー　→ 「どのカラムで結合するか」を指定
WHERE　条件式;
```
テーブル１：testA
| 氏名 | 出席番号 | 数学 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 50 |
| 佐藤 | 2 | 60 |
| 高橋 | 3 | 40 |
| 田中 | 4 | 80 |

テーブル２：testB
| 氏名 | 出席番号 | 国語 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 70 |
| 佐藤 | 2 | 60 |
| 伊藤 | 5 | 80 |
| 渡邊 | 6 | 50 |
```
SELECT　*  → すべてのカラムを指定
FROM　testA A　→ 別名をつけている
OUTER JOIN　testB B → 別名をつけている
ON　A.氏名 ＝ B.氏名　→ 両方のテーブルに存在する氏名のカラムを指定
```

上記のSQLを実行すると
| 氏名 | 出席番号 | 数学 | 氏名 | 出席番号 | 国語 |
| :-: | :-: | :-: | :-: | :-: | :-: |
| 鈴木 | 1 | 50 | 鈴木 | 1 | 70 |
| 佐藤 | 2 | 60 | 佐藤 | 2 | 60|
| 高橋 | 3 | 40 | NULL | NULL | NULL |
| 田中 | 4 | 80 | NULL | NULL | NULL |
| NULL | NULL | NULL | 伊藤 | 5 | 80 |
| NULL | NULL | NULL | 渡辺 | 6 | 50 |

ベン図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727339/cbc42bf3-ed34-ab77-cc19-7b3c27b1da78.png) 
このようにOUTER JOINでは、すべてのレコードが表示される。
on句で指定した氏名で、キーとして結合できなかったレコードは**NULL**として補完される。

## AND演算子　OR演算子（論理演算子）
**2つ以上の条件式を組み合わせてデータ抽出を行う際**に使用
### AND

```
SELECT　カラム名
FROM　テーブル名
WHERE　条件式A　AND　条件式B;　→ 条件式Aかつ条件式Bという条件が成立
```
例）
```
SELECT
    *
FROM
    product_table
WHERE
    商品分類 ='パンツ'　AND　単価 >= 5000;
```
上記のSQLを実行すると・・・
『商品分類がパンツ』、『単価が5000円以上』の**両方を満たす**レコードが抽出される。

ベン図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727339/abd9f703-ff1b-f7ff-2be5-d2b66d318212.png)

### OR

```
SELECT　カラム名
FROM　テーブル名
WHERE　条件式A　OR　条件式B; → 条件式Aまたは条件式Bという条件が成立
```
例）
```
SELECT
    *
FROM
    product_table
WHERE
    商品分類 ='パンツ'　OR　単価 >= 5000;
```
上記のSQLを実行すると・・・
『商品分類がパンツ』、『単価が5000円以上』の**どちらかを満たす**レコードがすべて抽出される。

ベン図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727339/80a4b263-c710-b070-9115-71f40884dc29.png)

### AND演算子とOR演算子を併用する場合
**AND演算子がOR演算子よりも優先**されるため、注意が必要

例）
『商品分類がパンツ』または、『単価が5,000円以上』かつ『売上金額が500，000円以上』という条件で抽出したい場合
下記のSQLを実行する
```
SELECT
    *
FROM
    product_table
WHERE
    （商品分類 ='パンツ'　OR　単価 >= 5000） AND 売上金額 >= 500000;
```
**（）の中の条件式が優先**されるため、意図した通りに実行結果が得られる。

## LIKE演算子
パターンマッチングを行い、
**データ群の中からある特定のパターンに一致あるいは類似するデータを探し出す**ことができる。

```
SELECT　カラム名
FROM　テーブル名
WHERE　カラム　LIKE　パターンマッチングの条件;　→ ワイルドカードを使用する
```
:::note info
ワイルドカード
_ (アンダーバー) : 任意の１文字の文字列（文字数を指定したい場合に使用）
％ : ０文字以上の任意の文字列
:::

例）テニスボール、サッカーボール、バレーボール、ボウリングと４つの球があったとする。
『％ボール』とすると・・・テニスボール、サッカーボール、バレーボールの３つの球が抽出される。
『___（アンダーバー３つ）ボール』とすると・・・テニスボール、バレーボールの２つのみ抽出される。

### 前方一致
例）
countryテーブル
| ID | 国名 | 首都 |
| :-: | :-: | :-: |
| １ | Argentina | Buenos Aires |
| ２ | Australia | Canberra |
| ３ | Belgium | Brussels |
| ４ | Japan | Tokyo |
| ５ | China | Beijing |

国名が『Ａ』から始まるレコードを抽出する場合
```
SELEC　 *
FROM　country
WHERE　国名 LIKE　‘A%’;
```
上記のSQLを実行すると・・・Argentina、Australiaの２つの国が抽出される

### 後方一致
例）
countryテーブル
| ID | 国名 | 首都 |
| :-: | :-: | :-: |
| １ | Argentina | Buenos Aires |
| ２ | Australia | Canberra |
| ３ | Belgium | Brussels |
| ４ | Japan | Tokyo |
| ５ | China | Beijing |

国名が『a』で終わるレコードを抽出する場合
```
SELEC　 *
FROM　country
WHERE　国名 LIKE　‘%a’;
```
上記のSQLを実行すると・・・Argentina、Australia、Chinaの３つの国が抽出される

### 前方一致かつ後方一致
例）
countryテーブル
| ID | 国名 | 首都 |
| :-: | :-: | :-: |
| １ | Argentina | Buenos Aires |
| ２ | Australia | Canberra |
| ３ | Belgium | Brussels |
| ４ | Japan | Tokyo |
| ５ | China | Beijing |

国名が『C』から始まり、『a』で終わるレコードを抽出する場合
```
SELEC　 *
FROM　country
WHERE　国名 LIKE　‘A%a’;
```
上記のSQLを実行すると・・・Chinaのみ抽出される

### 部分一致
例）
countryテーブル
| ID | 国名 | 首都 |
| :-: | :-: | :-: |
| １ | Argentina | Buenos Aires |
| ２ | Australia | Canberra |
| ３ | Belgium | Brussels |
| ４ | Japan | Tokyo |
| ５ | China | Beijing |

国名に『a』を含むレコードを抽出する場合
```
SELEC　 *
FROM　country
WHERE　国名 LIKE　‘%a%’;
```
上記のSQLを実行すると・・・Argentina、Australia、Japan、Chinaの４つの国が抽出される

### NOT　LIKE
例）
countryテーブル
| ID | 国名 | 首都 |
| :-: | :-: | :-: |
| １ | Argentina | Buenos Aires |
| ２ | Australia | Canberra |
| ３ | Belgium | Brussels |
| ４ | Japan | Tokyo |
| ５ | China | Beijing |

国名に『a』を含まないレコードを抽出する場合
```
SELEC　 *
FROM　country
WHERE　国名 NOT LIKE　‘%a%’;
```
上記のSQLを実行すると・・・Belgiumのみが抽出される

### ワイルドカードのエスケープ処理
例）
productテーブル
| ID | 商品ID | 商品 |
| :-: | :-: | :-: |
| １ | A_1000 |	Tシャツ |
| ２ | A_2000 |	ジーンズ |
| ３ | A_2010 | ショートパンツ |
| ４ | B_1000 |	ロングパンツ |
| ５ | B_1010 | ダウンジャケット |

商品IDに『_1』を含むレコードを抽出する場合
```
SELEC　 *
FROM　 product
WHERE　商品ID LIKE　‘%?_1%’ ESCAPE ‘?’;
```
上記のSQLを実行すると・・・A_1000のみが抽出される

ワイルドカードの『 % 』や『 _ 』は、特殊文字になるため、
WHERE　商品ID LIKE　‘%_1%’このような記述では意図したレコードは抽出されない。
エスケープ処理を忘れないように。

:::note info
特殊文字とは・・・
プログラム上で何らかの意味を持ち、そのままでは文字列として扱われない文字のこと
:::

## BETWEEN
**範囲指定をするとき**に使用。
範囲指定できるもの：数値・日付・時間・文字列など

```
SELECT　カラム名
FROM　テーブル名
WHERE　カラム名　BETWEEN 下限値　AND　上限値;
```

上記構文は、
```
SELECT　カラム名
FROM　テーブル名
WHERE　カラム名　>= 下限値　AND　[カラム名]　<=　上限値;
```
と同様になるため、指定範囲の値を条件として抽出できる。

**NOT BETWEEN**
```
SELECT　カラム名
FROM　テーブル名
WHERE　カラム名　NOT　BETWEEN 下限値　AND　上限値;
```
指定範囲外の値を条件にして抽出できる。

例）bakery_shop
| 商品名 | 売上金額 | 売上日 |
| :-: | :-: | :-: |
| クリームパン |	5,000 |	2022-10-17 |
| 食パン | 3,000 | 2022-10-25 |
| あんぱん |	6,000 |	2022-10-07 |
| カレーパン | 4,000 | 2022-10-30 |
| メロンパン | 3,500 | 2022-11-01 |

『売上金額が、4,000円以上6,000円以下』のデータを抽出するには下記のSQLを実行する。

``SELECT 商品名, 売上金額 FROM bakery_shop WHERE 売上金額 BETWEEN 4000 AND 6000;``

実行結果は下記になる。
| 商品名 | 売上金額 |
| :-: | :-: |
| クリームパン | 5,000 |
| カレーパン | 4,000 |
| あんぱん | 6,000 |

『売上日が、2022-10-17から2022-10-30まで』のデータを抽出するには下記のSQLを実行。

``SELECT * FROM bakery_shop WHERE 売上日 BETWEEN ‘2022-10-17’ AND ‘2022-10-30’;``

実行結果（すべてのカラムを選択しているので、売上金額も抽出される）
| 商品名 | 売上金額 | 売上日 |
| :-: | :-: | :-: |
| クリームパン | 5,000 | 2022-10-17 |
| 食パン | 3,000 | 2022-10-25 |
| カレーパン | 4,000 | 2022-10-30 |

『売上金額が、4,000円以上6,000円以下』以外のデータを抽出するには下記のSQLを実行する。

``SELECT 商品名, 売上金額 FROM bakery_shop WHERE 売上金額 NOT BETWEEN 4000 AND 6000;``

実行結果は下記になり、NOT BETWEENは指定された範囲外の抽出に使用できる。
| 商品名	| 売上金額 |
| :-: | :-: |
| 食パン | 3,000 |
| メロンパン | 3,500 |

## LENGTH関数
LENGTH関数は、**引数に指定した文字列の文字数を取得**。

LENGTH（引数）
引数・・・**文字列**：文字数
&emsp;&emsp;&emsp;&emsp;&emsp;**BLOB型**：バイト数
&emsp;&emsp;&emsp;&emsp;&emsp;上記以外：文字列型に変換した文字数
&emsp;&emsp;&emsp;&emsp;&emsp;**NULL**：NULL

:::note info
BLOB型はバイナリデータの格納に使用されるデータ型で、BLOBとはBinary Large Objectという意味。
:::

BLOB型の例は、画像や音声などのマルチメディアオブジェクトをバイナリデータに変換したもの。

引数が文字列やBLOB型でない場合は、一度文字列に変換したうえで文字数を取得。

引数がNULLの場合は、NULLが返ってくる。

```
SELECT LENGTH(カラム) → カラムの値の文字数を取得
FROM テーブル;
```

例）bakery_shop
| 商品名 | 売上金額 |売上日 |
| :-: | :-: | :-: |
| クリームパン | 5,000 | 2022-10-17 |
| あんぱん | 6,000 | 2022-10-07 |
| カレーパン | 4,000 | 2022-10-30 |
| メロンパン | 3,500 | 2022-11-01 |

```
SELECT 
    商品名, LENGTH(商品名)文字数 → 商品名のカラムと商品名の文字数を新しいカラムの『文字数』で取得
FROM 
    bakery_shop;
```

上記SQLを実行
| 商品名 | 文字数 |
| :-: | :-: |
| クリームパン | 6 |
| あんぱん | 4 |
| カレーパン | 5 |
| メロンパン | 5 |

指定した文字数以上のデータを抽出する場合は、条件抽出するためのWHERE句で使用
```
SELECT 
   商品名, LENGTH(商品名)文字数
FROM 
   bakery_shop
WHERE
   LENGTH(商品名) >= 5; → 5文字以上のデータを抽出する
```

SQLを実行
| 商品名 | 文字数 |
| :-: | :-: |
| クリームパン | 6 |
| カレーパン | 5 |
| メロンパン | 5 |

５文字以上のデータを抽出された。

## DISTINCT
**値の重複を排除してカラムを抽出**したいときに使用。

```
SELECT 
   distinct カラム１, カラム２,… → distinctを使って抽出したカラムは一意の値だけ残る
FROM 
   テーブル;
```

例) foods_table
| 商品分類 |	商品名 |	価格 |
| :-: | :-: | :-: |
| パン | カレーパン | 200 |
| 弁当 | からあげ弁当 | 500 |
| パン | カレーパン | 150 |
| 飲料 | 水 | 100 |

``SELECT distinct 商品分類 FROM foods_table;``
を実行すると
| 商品分類 |
| :-: |
| パン |
| 弁当 |
| 飲料 |

のように、値の重複（パンとパン）を排除して抽出される。

``SELECT distinct 商品分類,　商品名 FROM foods_table;``
と複数カラムを指定することもできる。
上記を実行すると
| 商品分類 |	商品名 |
| :-: | :-: |
| パン | カレーパン |
| 弁当 | からあげ弁当 |
| 飲料 | 水 |

のように、値の重複（パンとパン、カレーパンとカレーパン）を排除して抽出される。

**count関数**と併用することもできる
``SELECT count(distinct 商品分類) FROM foods_table;``
を実行すると
| count(distinct 商品分類) |
| :-: |
| 3 |

となり、商品分類の重複を排除した『パン・弁当・飲料』の３種類存在するため、３という値が抽出される。

:::note info
**GROUP BYとの違い**
DISTINCTは、『重複した値を排除』
GROUP BY句は、『重複した値を一つにまとめる』というイメージ。
:::

例）
+ DISTINCT
``SELECT distinct 商品分類 FROM foods_table;``
+ GROUP BY
``SELECT 商品分類 FROM foods_table GROUP BY 商品分類;``

上記SQL構文は、下記の結果になり、同じ結果が得られる。

| 商品分類 |
| :-: |
| パン |
| 弁当 |
| 飲料 |

DISTINCTもGROUP BYも一意の値が抽出されるのは、変わらないため、値の重複なしで抽出される。
しかし、GROUP BY句を使った集計関数は、DISTINCTで書き換えることはできない。

``SELECT 商品分類, sum(価格) FROM foods_table GROUP BY 商品分類;``

のように、GROUP BY句で商品分類のカラムを指定して、商品分類のカラムの値ごとに価格のカラムをSUM関数で集計する処理は、DISTINCTでは向いていないため、GROUP BY句を使いましょう。

## SUBSTRING関数
対象の文字列から、開始位置と文字数を指定して、**一部文を切り出す**ことができる関数。

```
substring（文字列（もしくは、カラム名）, 開始位置, 文字数）
```
**第一引数**：対象の文字列もしくは、カラムを指定。
**第二引数**：文字列を切り出す開始位置を整数で指定。
**第三引数**：開始位置から切り出したい文字数を指定。

:::note info
第二引数について
＋３であれば、最初の文字を１番目と数え、３文字目が開始位置になる。
－３であれば、最後の文字を１番目と数え、３文字前が開始位置になる。
:::

例）bakery_shop
| 商品名 | 売上金額 | 売上日 |
| :-: | :-: | :-: |
| クリームパン |	5,000 |	2022-10-17 |
| あんぱん |	6,000 |	2022-10-07 |
| カレーパン | 4,000 | 2022-11-02 |
| メロンパン | 3,500 | 2022-11-30 |

売上日の月ごとの売上金額の合計を出したい場合
```
SELECT
   substring(“売上日”, 6, 2) as 月,
   sum(“売上金額”) as 売上合計
FROM
   bakery_shop
GROUP BY 月;
```
上記SQLを実行すると
| 月 | 売上金額 |
| :-: | :-: |
| 10 | 11,000 |
| 11 | 7,500 |

売上金額を月ごとに集計できた。

## ROUND関数
数値を指定した**桁数の四捨五入**に使用。

```
ROUND（数値, 桁数）
```
**第一引数**：四捨五入の対象となる数値もしくはカラムを指定
　　　　　カラムを指定した場合、カラムのすべての数値が四捨五入の対象になる。
**第二引数**：四捨五入を行う桁数を指定
　　　　　桁数を指定しない場合、０が指定されたものとなり整数が表示される

:::note info
第一引数、第二引数のどちらかにNULLが入ると、NULLを返す
:::

例）test-results_table
あるクラスのテスト結果の平均点数を表すテーブル
| 科目名 | 平均点数 |
| :-: | :-: |
| 数学 | 60.25 |
| 国語 | 72.333 |
| 社会 | 65.5 |
| 英語 | 55.2 |
| 化学 | 58.25 |

+ 少数を省力し、整数のみ表示する場合
```
SELECT 
科目名,平均点数, ROUND(平均点数) as 四捨五入値　→ 第二引数を省略もしくは０とする
FROM
　　　test-results_table;
```

上記SQLを実行すると
| 科目名 | 平均点数 | 四捨五入値 |
| :-: | :-: | :-: |
| 数学 | 60.25 | 60 |
| 国語 | 72.333 | 72 |
| 社会 | 65.5 |	66 |
| 英語 | 55.2 |	55 |
| 化学 | 58.25 | 58 |

第一小数点が四捨五入され、整数のみ抽出される。

+ 第一小数点まで表示する
```
SELECT 
科目名,平均点数, ROUND(平均点数, 1) as 四捨五入値　→ 第二引数を1とする
FROM
　　test-results_table;
```

上記SQLを実行すると
| 科目名 | 平均点数 | 四捨五入値 |
| :-: | :-: | :-: |
| 数学 | 60.25 | 60.3 |
| 国語 | 72.333 | 72.3 |
| 社会 | 65.5 |	65.5 |
| 英語 | 55.2 |	55.2 |
| 化学 | 58.25 | 58.3 |

第二小数点が四捨五入され、第一小数点でまで表示される。

+ 第二小数点まで表示する
```
SELECT 
科目名,平均点数, ROUND(平均点数, 2) as 四捨五入値　→ 第二引数を２とする
FROM
　　test-results_table;
```
上記SQLを実行すると
| 科目名 | 平均点数 | 四捨五入値 |
| :-: | :-: | :-: |
| 数学 | 60.25 | 60.25 |
| 国語 | 72.333 | 72.33 |
| 社会 | 65.5 |	65.5 |
| 英語 | 55.2 |	55.2 |
| 化学 | 58.25 | 58.25 |

第三小数点が四捨五入され、第二小数点まで表示される。

## CASE式
**If Else構文のような条件分岐**を行いたいときに使用。

CASE式には２種類の条件分岐の式がある。
**単純CASE式**
カラムの値が指定した値と等価であるか判定し、処理の分岐を行う。
```
CASE カラム → 対象になるカラムを書く
    WHEN 値１ THEN 結果１　→ カラムの値に基づき処理を分岐（WHEN句に等価の場合、THENの値）
    WHEN 値２ THEN 結果２
    WHEN 値３ THEN 結果３
　　ELSE 結果４　→ WHEN句で指定した値のどれも一致しない場合の結果を、ELSE句に書く
END
```

**検索CASE式**
等価判定以外にもさまざまな条件で処理を分岐させる。
```
CASE → 対象となるカラムを書かない
    WHEN 条件式１ THEN 結果１　→ WHEN句で条件式を書いて、条件式がtrueのときTHENを返す
    WHEN 条件式２ THEN 結果２
    WHEN 条件式３ THEN 結果３
　　ELSE 結果４
END
```

例）test-results_table
あるクラスのテスト評価を氏名ごとに記載されたテーブル
| 氏名 | 英語（5点評価） |
| :-: | :-: | 
| 田中 | 5 |
| 鈴木 | 3 |
| 高橋 | 3 |
| 伊藤 | 1 |
| 佐藤 | 4 |

評価に基づいて成績を１であればC、５であればA、それ以外であればBと振り分けるとする。

+ 単純CASE式の場合
```
SELECT *
      CASE 英語 → 対象カラムを書く
　　　　　WHEN 1 THEN ‘C’
　　　　　WHEN 5 THEN ‘A’
      END 成績　→ 別名をつける場合はENDの後に
FROM test-results_table;
```
| 氏名 | 英語（5点評価） | 成績 |
| :-: | :-: | :-: |
| 田中 | 5 | A |
| 鈴木 | 3 | B |
| 高橋 | 3 | B |
| 伊藤 | 1 | C |
| 佐藤 | 4 | B |

このように、条件通り抽出できる。

+ 検索CASE式の場合
```
SELECT *
      CASE
　　　　　WHEN 英語 = 1 THEN ‘C’　→ 条件式にカラムを指定する
　　　　　WHEN 英語 = 5 THEN ‘A’
      END 成績　→ 別名をつける場合はENDの後に
FROM test-results_table;
```

| 氏名 | 英語（5点評価） | 成績 |
| :-: | :-: | :-: |
| 田中 | 5 | A |
| 鈴木 | 3 | B |
| 高橋 | 3 | B |
| 伊藤 | 1 | C |
| 佐藤 | 4 | B |

このように、条件通り抽出できる

+ CASE式を入れ子として使用する
products_table

| 商品名 | 売上日 | 売上金額 |
| :-: | :-: | :-: |
| ロングパンツ |	2022-10-20 | 9000 |
| ジーンズ |	2022-10-05 | 10000 |
| セーター |	2022-11-10 | 3000 |
| ジャケット | 2022-11-15 | 2000 |
| ショートパンツ | 2022-10-01 | 2000 |

10月中の売上に対してのみ、売上金額が1万円以上には、3,000円のキャッシュバック、8,000円以上には2,000円のキャッシュバック、それ以外は500円のキャッシュバック、10月以外はキャッシュバックを０とする。

```
SELECT *
     CASE
        WHEN 売上日 BETWEEN ‘2022-10-1’ AND ‘2022-10-31’ THEN
            CASE
               WHEN 売上金額 >= 10000 THEN 3000
               WHEN 売上金額 >= 8000 THEN 2000
               ELSE 0
            END
         ELSE 0
     END キャッシュバック
FROM products_table;
```

上記SQL実行すると
| 商品名 | 売上日 | 売上金額 | キャッシュバック |
| :-: | :-: | :-: | :-: |
| ロングパンツ |	2022-10-20 | 9000 |	2000 |
| ジーンズ |	2022-10-05 | 10000 | 3000 |
| セーター |	2022-11-10 | 3000 |	0 |
| ジャケット | 2022-11-15 | 2000 | 0 |
| ショートパンツ | 2022-10-01 | 2000 | 500 |

想定通りの結果が得られた。
このように、CASE式を入れ子にすることも可能。

## サブクエリ
一つのSQLの実行結果をもとに、また別のSQLで抽出を行いたい時、
**2段階以上の集計を1つのクエリに簡単にまとめる**ことができる。


サブクエリは、１つのSQLをカッコで括ったもの。
```
SELECT 
   カラムA, SUM(カラムB<integer>)
FROM
   (SELECT 　→ サブクエリ
      カラムA, カラムB<integer>
    FROM 
      テーブル
    WHERE 
      条件式)
GROUP BY カラムA
```
テーブルからAというカラムとBという数値が入ったカラムを条件式によって抽出され、FROM句でカラムAごとの数値の合計を計算。
```
SELECT 
   テーブルC.カラムA,
   テーブルC.カラムB,
   テーブルD.カラムE
FROM
  (SELECT 　→ サブクエリ
      カラムA,カラムB
   FROM 
      テーブルC
   WHERE 
      条件式)
JOIN 
   テーブルD
ON 
  テーブルC.カラムA = テーブルD.カラムA;
```
テーブルCからカラムAとBを抽出、これをJOIN句でテーブルDと結合し、テーブルCとカラムAとB、テーブルDのカラムEを抽出している。

例）
テーブル１：testA
| 氏名 | 出席番号 | 数学 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 50 |
| 佐藤 | 2 | 60 |
| 高橋 | 3 | 40 |
| 田中 | 4 | 80 |

テーブル２：testB
| 氏名 | 出席番号 | 数学 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 70 |
| 佐藤 | 2 | 60 |
| 高橋 | 3 | 80 |
| 田中 | 4 | 50 |

数学の点数が60点以下の人の国語の点数がいくつなのか抽出するには、以下のSQLを実行する
```
SELECT 
    testA.氏名,
    testB.出席番号,
    数学,
    国語
FROM
   (SELECT → testAのテーブルから数学の点数が６０点以下の人を抽出
      *
    FROM 
      testA
    WHERE 
      数学 <= 60) 
    testA
JOIN 　→ testBのテーブルを、どちらのテーブルにもある出席番号で結合
    testB
on 
    testA.出席番号 = testB.出席番号;
```
| 氏名 | 出席番号 |	数学 | 国語 |
| :-: | :-: | :-: | :-: |
| 鈴木 | 1 | 50 | 70 |
| 佐藤 | 2 | 60 | 60 |
| 高橋 | 3 | 40 | 80 |

となり、サブクエリの条件式を加えたデータ抽出が行える。

## IN句
**複数のOR条件を1つ**にまとめて記述でき、
**カラムの値が指定した値のリストのいずれかに一致する**という条件を作ることができる。

```
SELECT 
   * 
FROM 
   テーブル
WHERE 
   カラム IN (値1,値2,値3...);
```
WHERE句で条件を適用するカラムを記述し、IN句を記述する。

逆に、値のいずれにも一致しないという条件は下記になる。
```
SELECT 
   * 
FROM 
   テーブル
WHERE 
   カラム NOT IN (値1,値2,値3...);　→ カッコ内のカラムの値がいずれかに一致するとなる
```
例）
テーブル１：testA
| 氏名 | 出席番号 |	数学 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 50 |
| 佐藤 | 2 | 60 |
| 高橋 | 3 | 40 |
| 田中 | 4 | 80 |

氏名が鈴木、佐藤、今野のいずれかで当てはまるレコードを抽出したい場合
```
SELECT
     *
FROM
     testA
WHERE
     氏名　IN(‘鈴木’, ‘佐藤’, ‘今野’);　→ WHERE句にIN句で複数の条件を指定する
```
上記SQLを実行すると
| 氏名 | 出席番号 |	数学 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 50 |
| 佐藤 | 2 | 60 |

IN句で指定した条件のみ抽出される。

氏名が鈴木、佐藤、今野のいずれかにも当てはまらないレコードを抽出したい場合
```
SELECT
     *
FROM
     testA
WHERE
     氏名　NOT IN(‘鈴木’, ‘佐藤’, ‘今野’);　→ WHERE句にIN句で複数の条件を指定する
```
| 氏名 | 出席番号 |	数学 |
| :-: | :-: | :-: |
| 高橋 | 3 | 40 |
| 田中 | 4 | 80 |

と氏名が鈴木、佐藤、今野以外の氏名の方が抽出される。

### サブクエリを使用する場合
テーブル１：testA
| 氏名 | 出席番号 |	数学 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 50 |
| 佐藤 | 2 | 60 |
| 高橋 | 3 | 40 |
| 田中 | 4 | 80 |

テーブル２：testB
| 氏名 | 出席番号 |	国語 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 70 |
| 佐藤 | 2 | 60 |
| 高橋 | 3 | 80 |
| 田中 | 4 | 50 |

testBで国語の点数が70点以上のtestAの氏名のいずれかに一致するというレコードを抽出したい場合
```
SELECT 
　　*
FROM
    testA
WHERE
   氏名　IN(SELECT 氏名 FROM testB WHERE 国語 >= 70);　→ サブクエリで、testBの国語の点数が70点以上の氏名を抽出
```
| 氏名 | 出席番号 |	数学 |
| :-: | :-: | :-: |
| 鈴木 | 1 | 50 |
| 高橋 | 3 | 40 |

国語の点数が70点以上は、鈴木と高橋のみであるため、上記のレコードが抽出される。

## LIMIT
SQLを実行する時に**取得するデータの最大数を設定**できる関数。
```
SELECT
    カラム名
FROM
　　テーブル
LIMIT
　　設定レコード数;　→ LIMIT関数で設定したレコードより少ない場合、全データが抽出される
```

### OFFSET関数との組み合わせ
OFFSET関数は、**データを取得する開始位置を指定**することができる関数のこと

```
SELECT 
    カラム名
FROM
　　テーブル
LIMIT
　　設定レコード数 OFFSET 取得開始行 － 1;　→ 取得したい行数からマイナス１した数を指定
```

例）
「売上金額の合計」が高い商品順の4番目から３レコードのデータを抽出したい場合
```
SELECT
    商品名, 
SUM(売上金額) as 売上合計 → 売上金額の合計を抽出
FROM
　　Products_table
GROUP BY
    商品名　→ 売上金額を商品ごとにグループにして集計
ORDER BY 
　　売上合計 desc　→ 売上金額を高い順に並び替える
LIMIT 3 OFFSET 3;　→ レコード数を３、４番目を開始位置として指定したいので、４番目のマイナス１である３を指定
```