SQL 第2版 ゼロからはじめるデータベース操作 アウトプット（第9回）

## 概要
- 本記事は[SQL 第2版 ゼロからはじめるデータベース操作](https://www.shoeisha.co.jp/book/detail/9784798144450)で学んだ内容をまとめたものです。
  - 前回は「**第5章：複雑な問い合わせ**」 で学んだ内容をまとめました：[SQL 第2版 ゼロからはじめるデータベース操作 アウトプット（第8回）](https://qiita.com/takumi_links/items/37761f00ba7a73d8c239)
  - 今回は「**第6章：関数、述語、CASE式**」についてまとめていきます！

### 関数
- 関数とは、「ある値を入力すると、それに対応した値を出力する」機能のこと
  - この時の入力を引数（パラメータ）と呼び、出力を戻り値と呼ぶ

#### 算術関数
- +、-、*、/ 算術演算子を使って計算する

```sql
mysql> CREATE TABLE SampleMath
    -> (m  NUMERIC (10,3),
    ->  n  INTEGER,
    ->  p  INTEGER);

Query OK, 0 rows affected (0.08 sec)

mysql> START TRANSACTION;
Query OK, 0 rows affected (0.00 sec)

mysql> INSERT INTO SampleMath(m, n, p) VALUES (500,  0,    NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO SampleMath(m, n, p) VALUES (-180, 0,    NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO SampleMath(m, n, p) VALUES (NULL, NULL, NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO SampleMath(m, n, p) VALUES (NULL, 7,    3);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO SampleMath(m, n, p) VALUES (NULL, 5,    2);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO SampleMath(m, n, p) VALUES (NULL, 4,    NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO SampleMath(m, n, p) VALUES (8,    NULL, 3);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO SampleMath(m, n, p) VALUES (2.27, 1,    NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO SampleMath(m, n, p) VALUES (5.555,2,    NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO SampleMath(m, n, p) VALUES (NULL, 1,    NULL);
Query OK, 1 row affected (0.00 sec)

mysql> INSERT INTO SampleMath(m, n, p) VALUES (8.76, NULL, NULL);
Query OK, 1 row affected (0.00 sec)

mysql> 
mysql> COMMIT;
Query OK, 0 rows affected (0.01 sec)

mysql> SELECT * FROM SampleMath;
+----------+------+------+
| m        | n    | p    |
+----------+------+------+
|  500.000 |    0 | NULL |
| -180.000 |    0 | NULL |
|     NULL | NULL | NULL |
|     NULL |    7 |    3 |
|     NULL |    5 |    2 |
|     NULL |    4 | NULL |
|    8.000 | NULL |    3 |
|    2.270 |    1 | NULL |
|    5.555 |    2 | NULL |
|     NULL |    1 | NULL |
|    8.760 | NULL | NULL |
+----------+------+------+
11 rows in set (0.00 sec)
```

- 絶対値：ABS
  - 数値の符号を考えない、ゼロからの距離の大きさを表す数値
  - 関数の引数がNULLの場合は、戻り値もNULLになる

```sql
mysql> SELECT m, ABS(m) AS abs_col FROM SampleMath;
+----------+---------+
| m        | abs_col |
+----------+---------+
|  500.000 | 500.000 |
| -180.000 | 180.000 |
|     NULL |    NULL |
|     NULL |    NULL |
|     NULL |    NULL |
|     NULL |    NULL |
|    8.000 |   8.000 |
|    2.270 |   2.270 |
|    5.555 |   5.555 |
|     NULL |    NULL |
|    8.760 |   8.760 |
+----------+---------+
11 rows in set (0.01 sec)

```

- MOD：剰余
  - 割り算の余りを求める
  - **SQL Serverでは、剰余演算子は`%`を使う**

```sql
mysql> SELECT n, p,
    ->        MOD(n, p) AS mod_col
    ->   FROM SampleMath;
+------+------+---------+
| n    | p    | mod_col |
+------+------+---------+
|    0 | NULL |    NULL |
|    0 | NULL |    NULL |
| NULL | NULL |    NULL |
|    7 |    3 |       1 |
|    5 |    2 |       1 |
|    4 | NULL |    NULL |
| NULL |    3 |    NULL |
|    1 | NULL |    NULL |
|    2 | NULL |    NULL |
|    1 | NULL |    NULL |
| NULL | NULL |    NULL |
+------+------+---------+
11 rows in set (0.01 sec)
```

- ROUND: 四捨五入
  - `ROUND(数値,丸めの桁数)`
  - 丸めの桁数を1にすると小数点第1位で四捨五入される
  - 丸めの桁数を2にすると小数点第2位で四捨五入される

```sql
mysql> SELECT m, n, ROUND(m, n) AS round_col FROM SampleMath;
+----------+------+-----------+
| m        | n    | round_col |
+----------+------+-----------+
|  500.000 |    0 |   500.000 |
| -180.000 |    0 |  -180.000 |
|     NULL | NULL |      NULL |
|     NULL |    7 |      NULL |
|     NULL |    5 |      NULL |
|     NULL |    4 |      NULL |
|    8.000 | NULL |      NULL |
|    2.270 |    1 |     2.300 |
|    5.555 |    2 |     5.560 |
|     NULL |    1 |      NULL |
|    8.760 | NULL |      NULL |
+----------+------+-----------+
11 rows in set (0.01 sec)
```

#### 文字列関数
- 文字の連結
  - SQL Serverでは、+(プラス)演算子を使う
  - MySQLでは、CONCAT関数を使う

```sql
mysql> SELECT str1, str2, str3, CONCAT(str1, str2, str3) AS str_concat FROM SampleStr;
+--------------------+-----------+--------+--------------------------+
| str1               | str2      | str3   | str_concat               |
+--------------------+-----------+--------+--------------------------+
| あいう             | えお      | NULL   | NULL                     |
| abc                | def       | NULL   | NULL                     |
| 山田               | 太郎      | です   | 山田太郎です             |
| aaa                | NULL      | NULL   | NULL                     |
| NULL               | あああ    | NULL   | NULL                     |
| @!#$%              | NULL      | NULL   | NULL                     |
| ABC                | NULL      | NULL   | NULL                     |
| aBC                | NULL      | NULL   | NULL                     |
| abc太郎            | abc       | ABC    | abc太郎abcABC            |
| abcdefabc          | abc       | ABC    | abcdefabcabcABC          |
| ミックマック       | ッ        | っ     | ミックマックッっ         |
+--------------------+-----------+--------+--------------------------+
11 rows in set (0.01 sec)
```

- LENGTH: 文字列の長さ
  - 文字列の長さを求める
  - SQL Serverでは、LEN関数を使う
  - MySQLでは、LENGTH関数を使う
  - **文字列の長さは、バイト数で返される**
  - バイト数：1文字あたり1バイトの文字列は1バイト、2バイトの文字列は2バイト

- あいう（全角3文字）は9バイト
- abc（半角3文字）は3バイト
- 山田（全角2文字）は6バイト

```sql
mysql> SELECT str1, LENGTH(str1) AS len_str FROM SampleStr;
+--------------------+---------+
| str1               | len_str |
+--------------------+---------+
| あいう             |       9 |
| abc                |       3 |
| 山田               |       6 |
| aaa                |       3 |
| NULL               |    NULL |
| @!#$%              |       5 |
| ABC                |       3 |
| aBC                |       3 |
| abc太郎            |       9 |
| abcdefabc          |       9 |
| ミックマック       |      18 |
+--------------------+---------+
11 rows in set (0.00 sec)
```

- LOWER: 小文字化
  - アルファベットを小文字に変換する

```sql
mysql> SELECT str1, LOWER(str1) AS low_str FROM SampleStr;
+--------------------+--------------------+
| str1               | low_str            |
+--------------------+--------------------+
| あいう             | あいう             |
| abc                | abc                |
| 山田               | 山田               |
| aaa                | aaa                |
| NULL               | NULL               |
| @!#$%              | @!#$%              |
| ABC                | abc                |
| aBC                | abc                |
| abc太郎            | abc太郎            |
| abcdefabc          | abcdefabc          |
| ミックマック       | ミックマック       |
+--------------------+--------------------+
11 rows in set (0.01 sec)
```

- UPPER: 大文字化
  - アルファベットを大文字に変換する

```sql
mysql> SELECT str1, UPPER(str1) AS up_str FROM SampleStr WHERE str1 IN ('ABC', 'aBC', 'abc');
+------+--------+
| str1 | up_str |
+------+--------+
| abc  | ABC    |
| ABC  | ABC    |
| aBC  | ABC    |
+------+--------+
3 rows in set (0.01 sec)
```

- REPLACE: 文字列の置換
  - 文字列の一部を別の文字列に置き換える
  - `REPLACE(対象文字列, 検索文字列, 置換文字列)`

```sql
mysql> SELECT str1, str2, str3, REPLACE(str1, str2, str3) AS rep_str FROM SampleStr;
+--------------------+-----------+--------+--------------------+
| str1               | str2      | str3   | rep_str            |
+--------------------+-----------+--------+--------------------+
| あいう             | えお      | NULL   | NULL               |
| abc                | def       | NULL   | NULL               |
| 山田               | 太郎      | です   | 山田               |
| aaa                | NULL      | NULL   | NULL               |
| NULL               | あああ    | NULL   | NULL               |
| @!#$%              | NULL      | NULL   | NULL               |
| ABC                | NULL      | NULL   | NULL               |
| aBC                | NULL      | NULL   | NULL               |
| abc太郎            | abc       | ABC    | ABC太郎            |
| abcdefabc          | abc       | ABC    | ABCdefABC          |
| ミックマック       | ッ        | っ     | ミっクマっク       |
+--------------------+-----------+--------+--------------------+
11 rows in set (0.01 sec)
```

- SUBSTRING: 文字列の切り出し
  - 文字列の一部を切り出す
  - `SUBSTRING(対象文字列, 開始位置, 長さ)`
  - 開始位置は1から始まる

```sql
-- 文字列の左から3文字目から2文字を切り出す
mysql> SELECT str1, SUBSTRING(str1 FROM 3 FOR 2) AS sub_str FROM SampleStr;
+--------------------+---------+
| str1               | sub_str |
+--------------------+---------+
| あいう             | う      |
| abc                | c       |
| 山田               |         |
| aaa                | a       |
| NULL               | NULL    |
| @!#$%              | #$      |
| ABC                | C       |
| aBC                | C       |
| abc太郎            | c太     |
| abcdefabc          | cd      |
| ミックマック       | クマ    |
+--------------------+---------+
11 rows in set (0.00 sec)
```

##### 日付関数
- CURRENT_DATE: 現在の日付
  - 現在の日付を取得する
  - SQL Serverでは、GETDATE関数(`SELECT GETDATE();`）を使う

```sql 
mysql> SELECT CURRENT_DATE();
+----------------+
| CURRENT_DATE() |
+----------------+
| 2025-06-11     |
+----------------+
1 row in set (0.01 sec)
```

- CURRENT_TIME: 現在の時刻
  - 現在の時刻を取得する
  - SQL Serverでは、CURRENT_TIME関数(`SELECT CAST(CURRENT_TIMESTAMP AS time) AS CUR_TIME;`）を使う

```sql
mysql> SELECT CURRENT_TIME();
+----------------+
| CURRENT_TIME() |
+----------------+
| 04:52:34       |
+----------------+
1 row in set (0.00 sec)
```

- CURRENT_TIMESTAMP: 現在の日付と時刻
  - 現在の日付と時刻を取得する
  - SQL Serverも、CURRENT_TIMESTAMP関数(`SELECT CURRENT_TIMESTAMP;`）を使う

```sql
mysql> SELECT CURRENT_TIMESTAMP;
+---------------------+
| CURRENT_TIMESTAMP   |
+---------------------+
| 2025-06-11 04:53:29 |
+---------------------+
1 row in set (0.00 sec)
```

- EXTRACT: 日付の一部を抽出
  - 日付から年、月、日などの一部を抽出する
  - SQL Serverでは、DATEPART関数を使う
  - `EXTRACT(部分, 日付)`

```sql
mysql> SELECT CURRENT_DATE(), EXTRACT(YEAR FROM CURRENT_TIMESTAMP()) AS year,
         -> EXTRACT(MONTH FROM CURRENT_TIMESTAMP()) AS month,
         -> EXTRACT(DAY FROM CURRENT_TIMESTAMP()) AS day,
         -> EXTRACT(HOUR FROM CURRENT_TIMESTAMP) AS hour,
         -> EXTRACT(MINUTE FROM CURRENT_TIMESTAMP) AS minute,
         -> EXTRACT(SECOND FROM CURRENT_TIMESTAMP) AS second;
+----------------+------+-------+------+------+--------+--------+
| CURRENT_DATE() | year | month | day  | hour | minute | second |
+----------------+------+-------+------+------+--------+--------+
| 2025-06-11     | 2025 |     6 |   11 |    4 |     58 |      6 |
+----------------+------+-------+------+------+--------+--------+
1 row in set (0.01 sec)
```

```sql
-- SQL Serverでの例
SELECT CURRENT_TIMESTAMP,
       DATEPART(YEAR, CURRENT_TIMESTAMP) AS year,
       DATEPART(MONTH, CURRENT_TIMESTAMP) AS month,
       DATEPART(DAY, CURRENT_TIMESTAMP) AS day,
       DATEPART(HOUR, CURRENT_TIMESTAMP) AS hour,
       DATEPART(MINUTE, CURRENT_TIMESTAMP) AS minute,
       DATEPART(SECOND, CURRENT_TIMESTAMP) AS second;
```

#### 変換関数
- `CAST(値 AS 型)`
  - 値を指定した型に変換する
  - SQL Serverでも同様に、`CAST(値 AS 型)`を使う

```sql
mysql> SELECT CAST('0001' AS SIGNED INTEGER) AS int_col;
+---------+
| int_col |
+---------+
|       1 |
+---------+
1 row in set (0.00 sec)

mysql> SELECT CAST('2025-06-11' AS DATE) AS date_col;
+------------+
| date_col   |
+------------+
| 2025-06-11 |
+------------+
1 row in set (0.00 sec)
```

- COALESCE: NULL値の置換
  - 引数の中で最初にNULLでない値を返す
  - SQL Serverでも同様に、`COALESCE(値1, 値2, ...)`を使う

```sql
mysql> SELECT COALESCE(NULL, 1) AS col_1, COALESCE(NULL, 'test', NULL) AS col_2, COALESCE(NULL, NULL, '2025-06-11');
+-------+-------+------------------------------------+
| col_1 | col_2 | COALESCE(NULL, NULL, '2025-06-11') |
+-------+-------+------------------------------------+
|     1 | test  | 2025-06-11                         |
+-------+-------+------------------------------------+
1 row in set (0.01 sec)

mysql> SELECT COALESCE(str2, 'NULL!!') FROM SampleStr;
+--------------------------+
| COALESCE(str2, 'NULL!!') |
+--------------------------+
| えお                     |
| def                      |
| 太郎                     |
| NULL!!                   |
| あああ                   |
| NULL!!                   |
| NULL!!                   |
| NULL!!                   |
| abc                      |
| abc                      |
| ッ                       |
+--------------------------+
11 rows in set (0.00 sec)
```

### 述語
- 述語とは、戻り値が真偽値（TRUE/FALSE）になる関数のこと
  - `LIKE`, `BETWEEN`, `IS NULL`, `IS NOT NULL`, `IN`, `EXISTS` などがある

#### LIKE述語
- 文字列の部分一致を検索する
  - 前方一致：`LIKE 'abc%'`
  - 後方一致：`LIKE '%abc'`
  - 中間一致：`LIKE '%abc%'`
- 文字列そのものを「=」で指定するのではなく、文字列の中に含まれる規則に基づいて検索することを「**パターンマッチング**」と呼ぶ
- パターンマッチングでは、ワイルドカードを使うことができる
  - `%`：任意の文字列（0文字以上）
  - `_`：任意の1文字

```sql
CREATE TABLE SampleLike
( strcol VARCHAR(6) NOT NULL,
  PRIMARY KEY (strcol));

START TRANSACTION;

INSERT INTO SampleLike (strcol) VALUES ('abcddd');
INSERT INTO SampleLike (strcol) VALUES ('dddabc');
INSERT INTO SampleLike (strcol) VALUES ('abdddc');
INSERT INTO SampleLike (strcol) VALUES ('abcdd');
INSERT INTO SampleLike (strcol) VALUES ('ddabc');
INSERT INTO SampleLike (strcol) VALUES ('abddc');

COMMIT;

mysql> SELECT * FROM SampleLike WHERE strcol LIKE 'ddd%';
+--------+
| strcol |
+--------+
| dddabc |
+--------+
1 row in set (0.00 sec)

mysql> SELECT * FROM SampleLike WHERE strcol LIKE '%ddd%';
+--------+
| strcol |
+--------+
| abcddd |
| abdddc |
| dddabc |
+--------+
3 rows in set (0.00 sec)

mysql> SELECT * FROM SampleLike WHERE strcol LIKE '%ddd';
+--------+
| strcol |
+--------+
| abcddd |
+--------+
1 row in set (0.00 sec)

mysql> SELECT * FROM SampleLike WHERE strcol LIKE 'abc__';
+--------+
| strcol |
+--------+
| abcdd  |
+--------+
1 row in set (0.01 sec)

mysql> SELECT * FROM SampleLike WHERE strcol LIKE 'abc___';
+--------+
| strcol |
+--------+
| abcddd |
+--------+
1 row in set (0.00 sec)
```

#### BETWEEN述語
- 範囲検索を行う
  - `BETWEEN 開始値 AND 終了値`

```sql
-- BETWEEN述語は、開始値と終了値を含む
mysql> SELECT shohin_mei, hanbai_tanka FROM Shohin WHERE hanbai_tanka BETWEEN 100 AND 1000;
+--------------------+--------------+
| shohin_mei         | hanbai_tanka |
+--------------------+--------------+
| Tシャツ            |         1000 |
| 穴あけパンチ       |          500 |
| フォーク           |          500 |
| おろしがね         |          880 |
| ボールペン         |          100 |
+--------------------+--------------+
5 rows in set (0.00 sec)

mysql> SELECT shohin_mei, hanbai_tanka FROM Shohin WHERE hanbai_tanka > 100 AND hanbai_tanka < 1000;
+--------------------+--------------+
| shohin_mei         | hanbai_tanka |
+--------------------+--------------+
| 穴あけパンチ       |          500 |
| フォーク           |          500 |
| おろしがね         |          880 |
+--------------------+--------------+
3 rows in set (0.00 sec)
```

#### IS NULL, IS NOT NULL述語
- NULL値の判定を行う
  - `IS NULL`: NULL値であるかどうかを判定する
  - `IS NOT NULL`: NULL値でないかどうかを判定する

```sql
mysql> SELECT shohin_mei, shiire_tanka FROM Shohin WHERE shiire_tanka IS NULL;
+-----------------+--------------+
| shohin_mei      | shiire_tanka |
+-----------------+--------------+
| フォーク        |         NULL |
| ボールペン      |         NULL |
+-----------------+--------------+
2 rows in set (0.00 sec)

mysql> SELECT shohin_mei, shiire_tanka FROM Shohin WHERE shiire_tanka IS NOT NULL;
+-----------------------+--------------+
| shohin_mei            | shiire_tanka |
+-----------------------+--------------+
| Tシャツ               |          500 |
| 穴あけパンチ          |          320 |
| カッターシャツ        |         2800 |
| 包丁                  |         2800 |
| 圧力鍋                |         5000 |
| おろしがね            |          790 |
+-----------------------+--------------+
6 rows in set (0.00 sec)
```

#### IN述語
- 複数の値の中から一致する値を検索する
  - `IN (値1, 値2, ...)`
  - OR述語の代わりに使うことができる
- NULLを選択することはできない
  - NULLを含む場合は、IS NULL述語を使う必要がある

```sql
mysql> SELECT shohin_mei, shiire_tanka FROM Shohin WHERE shiire_tanka IN (320, 500, 5000);
+--------------------+--------------+
| shohin_mei         | shiire_tanka |
+--------------------+--------------+
| Tシャツ            |          500 |
| 穴あけパンチ       |          320 |
| 圧力鍋             |         5000 |
+--------------------+--------------+
3 rows in set (0.00 sec)

mysql> SELECT shohin_mei, shiire_tanka FROM Shohin WHERE shiire_tanka NOT IN (320, 500, 5000);
+-----------------------+--------------+
| shohin_mei            | shiire_tanka |
+-----------------------+--------------+
| カッターシャツ        |         2800 |
| 包丁                  |         2800 |
| おろしがね            |          790 |
+-----------------------+--------------+
3 rows in set (0.00 sec)
```

##### IN述語の応用
- IN述語は、サブクエリと組み合わせて使うことができる
  - サブクエリの結果をIN述語の値として使用する

```sql
CREATE TABLE TenpoShohin
(tenpo_id  CHAR(4)       NOT NULL,
 tenpo_mei  VARCHAR(200) NOT NULL,
 shohin_id CHAR(4)       NOT NULL,
 suryo     INTEGER       NOT NULL,
 PRIMARY KEY (tenpo_id, shohin_id));

START TRANSACTION;

INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000A',	'東京',		'0001',	30);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000A',	'東京',		'0002',	50);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000A',	'東京',		'0003',	15);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000B',	'名古屋',	'0002',	30);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000B',	'名古屋',	'0003',	120);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000B',	'名古屋',	'0004',	20);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000B',	'名古屋',	'0006',	10);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000B',	'名古屋',	'0007',	40);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000C',	'大阪',		'0003',	20);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000C',	'大阪',		'0004',	50);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000C',	'大阪',		'0006',	90);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000C',	'大阪',		'0007',	70);
INSERT INTO TenpoShohin (tenpo_id, tenpo_mei, shohin_id, suryo) VALUES ('000D',	'福岡',		'0001',	100);

COMMIT;

-- 大阪店(tenpo_id = '000C')で取り扱っている商品の販売単価を求める
mysql> SELECT shohin_id FROM TenpoShohin WHERE tenpo_id = '000C';
+-----------+
| shohin_id |
+-----------+
| 0003      |
| 0004      |
| 0006      |
| 0007      |
+-----------+
4 rows in set (0.00 sec)

mysql> SELECT shohin_mei, hanbai_tanka FROM Shohin WHERE shohin_id IN ( SELECT shohin_id FROM TenpoShohin WHERE tenpo_id = '000C');
+-----------------------+--------------+
| shohin_mei            | hanbai_tanka |
+-----------------------+--------------+
| カッターシャツ        |         4000 |
| 包丁                  |         3000 |
| フォーク              |          500 |
| おろしがね            |          880 |
+-----------------------+--------------+
4 rows in set (0.00 sec)

mysql> SELECT shohin_mei, hanbai_tanka FROM Shohin WHERE shohin_id NOT IN ( SELECT shohin_id FROM TenpoShohin WHERE tenpo_id = '000A');
+-----------------+--------------+
| shohin_mei      | hanbai_tanka |
+-----------------+--------------+
| 包丁            |         3000 |
| 圧力鍋          |         6800 |
| フォーク        |          500 |
| おろしがね      |          880 |
| ボールペン      |          100 |
+-----------------+--------------+
5 rows in set (0.00 sec)
```

#### EXISTS述語
- EXISTS術後の役割：「ある条件に合致するレコードの存在有無を調べる」こと
  - 存在すればTRUE、存在しなければFALSEを返す
- EXISTSの引数は常に**相関サブクエリ**でなければならない
  - 相関サブクエリとは、外側のクエリの値を参照するサブクエリのこと
- EXISTSの引数のサブクエリは常に**SELECT ***を指定する

```sql
-- 大阪店(tenpo_id = '000C')で取り扱っている商品の販売単価を求める
mysql> SELECT shohin_mei, hanbai_tanka FROM Shohin S1 WHERE EXISTS (SELECT * FROM TenpoShohin TS WHERE TS.tenpo_id = '000C' AND TS.shohin_id = S1.shohin_id);
+-----------------------+--------------+
| shohin_mei            | hanbai_tanka |
+-----------------------+--------------+
| カッターシャツ        |         4000 |
| 包丁                  |         3000 |
| フォーク              |          500 |
| おろしがね            |          880 |
+-----------------------+--------------+
4 rows in set (0.00 sec)

-- 東京店(tenpo_id = '000A')に置いてある商品以外の販売単価を求める
mysql> SELECT shohin_mei, hanbai_tanka FROM Shohin S1 WHERE NOT EXISTS (SELECT * FROM TenpoShohin TS WHERE TS.tenpo_id = '000A' AND TS.shohin_id = S1.shohin_id);
+-----------------+--------------+
| shohin_mei      | hanbai_tanka |
+-----------------+--------------+
| 包丁            |         3000 |
| 圧力鍋          |         6800 |
| フォーク        |          500 |
| おろしがね      |          880 |
| ボールペン      |          100 |
+-----------------+--------------+
5 rows in set (0.00 sec)
```

### CASE式
- CASE式は、条件分岐を行うための式
  - 単純CASE式と検索CASE式の2種類がある 
- 検索CASE式は、複数の条件を指定して、それぞれの条件に応じた値を返すことができる
  - `CASE WHEN 条件1 THEN 値1 WHEN 条件2 THEN 値2 ... ELSE デフォルト値 END`
- 行列変換：
  - 行列変換とは、行と列を入れ替えること
  - CASE式を使って、行列変換を行うことができる

```sql
-- CASE式で商品分類にA〜Cの文字列を割り当てる
mysql> SELECT
         ->     shohin_mei,
         ->     CASE
         ->        WHEN shohin_bunrui = '衣服'     THEN CONCAT('A: ', shohin_bunrui)
         ->        WHEN shohin_bunrui = '事務用品' THEN CONCAT('B: ', shohin_bunrui)
         ->        WHEN shohin_bunrui = 'キッチン用品' THEN CONCAT('C: ', shohin_bunrui) 
         ->        ELSE NULL
         ->     END AS abc_shohin_bunrui
         -> FROM Shohin;
+-----------------------+-------------------+
| shohin_mei            | abc_shohin_bunrui |
+-----------------------+-------------------+
| Tシャツ               | A: 衣服              |
| 穴あけパンチ          | B: 事務用品              |
| カッターシャツ        | A: 衣服              |
| 包丁                  | C: キッチン用品              |
| 圧力鍋                | C: キッチン用品              |
| フォーク              | C: キッチン用品              |
| おろしがね            | C: キッチン用品              |
| ボールペン            | B: 事務用品              |
+-----------------------+-------------------+
8 rows in set (0.01 sec)

-- 行列変換、商品分類ごとに販売単価を合計した結果を行列変換する
mysql> SELECT
                       ->     SUM(CASE WHEN shohin_bunrui = '衣服' THEN hanbai_tanka ELSE 0 END) AS sum_tanka_ifuku,
                       ->     SUM(CASE WHEN shohin_bunrui = 'キッチン用品' THEN hanbai_tanka ELSE 0 END) AS sum_tanka_kitchen,
                       ->     SUM(CASE WHEN shohin_bunrui = '事務用品' THEN hanbai_tanka ELSE 0 END) AS sum_tanka_office
                       -> FROM Shohin;
+-----------------+-------------------+------------------+
| sum_tanka_ifuku | sum_tanka_kitchen | sum_tanka_office |
+-----------------+-------------------+------------------+
|            5000 |             11180 |              600 |
+-----------------+-------------------+------------------+
1 row in set (0.00 sec)
```

### 練習問題を解いてみる
- 6.1 - 6.2を解いてみた内容を掲載

#### 6.1
- クエリの実行

```sql
mysql> CREATE VIEW ViewRenshu5_1 AS SELECT shohin_mei, hanbai_tanka, torokubi FROM Shohin WHERE hanbai_tanka >= 1000 AND torokubi = '2009-09-20';
Query OK, 0 rows affected (0.02 sec)

mysql> SELECT * FROM ViewRenshu5_1;
+------------+--------------+------------+
| shohin_mei | hanbai_tanka | torokubi   |
+------------+--------------+------------+
| Tシャツ    |         1000 | 2009-09-20 |
| 包丁       |         3000 | 2009-09-20 |
+------------+--------------+------------+
2 rows in set (0.00 sec)
```

#### 6-2
- 行列変換

```sql
-- 低額商品（販売単価が1000以下）、中額商品（販売単価が1000以上3000以下）、高額商品（販売単価が3001以上）の商品数を求める
mysql> SELECT SUM(CASE WHEN hanbai_tanka <= 1000 THEN 1 ELSE 0 END) AS low_price,
         -> SUM(CASE WHEN hanbai_tanka BETWEEN 1001 AND 3000 THEN 1 ELSE 0 END) AS mid_price,
         -> SUM(CASE WHEN hanbai_tanka >= 3001 THEN 1 ELSE 0 END) AS high_price FROM Shohin;
+-----------+-----------+------------+
| low_price | mid_price | high_price |
+-----------+-----------+------------+
|         5 |         1 |          2 |
+-----------+-----------+------------+
1 row in set (0.01 sec)
```

### まとめ
- 本記事では「**第6章：関数、述語、CASE式**」 についてまとめました！
- 次回は「**第7章：集合演算**」 についてまとめていきます！

## 参考
- [SQL 第2版 ゼロからはじめるデータベース操作](https://www.shoeisha.co.jp/book/detail/9784798144450)