SQL SELECT文の使い方入門

# SQLとは
SQLとは、データベースを操作（ローカルへのデータ取得やDBへのデータ追加等々）するためのプログラミング言語。本記事ではデータ取得機能（`SELECT`文）のみを説明する。

## 使用場面
SQLを使用するのは以下のようなケース:

- システムのDBからデータを取得する場合
- 条件を指定した抽出
- 簡単な計算（単位を変える等）


逆に以下のようなケースでは使用しない方がよい:

- 複雑な変形・計算 -> より簡単にできるPandasやPower Queryで

# `SELECT`文の基本構造
国別年月別の売上高をデータとする「売上」というテーブルがDBにあるとする:

| 国 | 年月 | 売上 |
|:-:|:-:|-:|
| 薩摩 | 21/6 | 12 |
| 薩摩 | 21/7 | 13 |
| 大隅 | 21/6 | 22 |
| 大隅 | 21/7 | 23 |
| 日向 | 21/6 | 32 |
| 日向 | 21/7 | 33 |


そこから薩摩と大隅のデータを取得するには次のようなSQL文となる:

```SQL
SELECT
    国, 年月, 売上  #`FROM`句で指定したテーブルに含まれる列のうち、使用する列を選択
FROM
    売上  #データ元テーブル
WHERE
    国 IN ("薩摩", "大隅")  #（省略可）抽出条件
ORDER BY
    売上  #（省略可）並べ替え順序
;
```

以下、このSQL文の各要素を説明する。

# 列選択
`SELECT`文の直後に記載。後述の`FROM`句で取得したテーブルからどの列を取得するかを指定する部分。
## 基本
- すべての列を選択する場合は`*`（アスタリスク）。
- 特定の列を選択するには、列名を`,`(カンマ)で区切って記載。
- 別名で使用したい場合は次のように、列名の後にスペースを空けて指定する:

    ```SQL
    SELECT
        国 country, 年月 YYMM, 売上  #DBによっては 年 AS yのようにASが使える 
    FROM
        売上
    ;
    ````
- 列単位で計算が可能→[Link](https://style.potepan.com/articles/26162.html#SQL)

    ```SQL
    SELECT
        売上 / POWER(10, 3) AS 売上_千円
    FROM
        売上
    ;
    ```

## 応用
- 関数を利用できる。`TO_DATE`関数の例:

    ```SQL
    # 約定日テーブルにあるtrading_date列（YYYYMMDDの数字8桁）を日付形式に変換して取得する
    SELECT
        TO_DATE(trading_date) AS 約定日
    FROM
        約定テーブル
    ;
    ```
- `DISTINCT`句で重複を削除

    ```SQL
    SELECT DISTINCT
        魚種 fish 
    FROM
        漁獲テーブル
    ;
    ```

# `WHERE`句
以下で紹介する様々な比較演算子の結果を、論理演算子（`AND`, `OR`, `NOT`等）と括弧（`(`、`)`）を組み合わせて、抽出条件を指定する。

## 比較演算子(基本)
|比較演算子|機能|使用例|
|:-:|:-|:-:|
| =     | A と B は等しい | A = B
| <=>   | A と B は等しい ※NULL可 | A <=> B
| <>    | A と B は等しくない | A <> B
| !=    | A と B は等しくない | A != B
| <     | A は B よりも小さい | A < B
| <=    | A は B 以下 | A <= B
| >     | A は B よりも大きい | A > B
| >=    | A は B 以上 | A >= B

## 比較演算子(応用)
|比較演算子|機能|使用例|
|:-:|:-|:-|
| IS [NOT] | A は Bである | ○ IS B
| IS [NOT] NULL | 	A は NULLである | A IS NULL
| [NOT] BETWEEN AND | 	A は min以上max以下 | A BETWEEN min AND max
| [NOT] IN | 	A は 括弧内のいずれかの値に等しい | A IN (B, C, …)
| ANY | 	A は 括弧内のいずれかの値に等しい | A ANY (B, C, …)
| [NOT] LIKE | 	正規表現を使用した文字列検索 | A LIKE B
| [NOT] REGEXP | 正規表現を使用した文字列検索| A REGEXP B 
| [NOT] RLIKE | 	REGEXPと同じ | A EGEXP B

# `FROM`句
どのテーブルを取得するかを指定する。複数のテーブルの場合は`JOIN`句で結合する。

## 基本
- 元になるテーブルを指定。
- 別名で使用したい場合は`SELECT`と同様、列名の後にスペースを空けて指定する。

## 応用
複数のテーブルの結合が可能。`JOIN`句での結合が必要になるが、ここではよく使う内部結合と左外部結合を説明する。

### 内部結合
二つのテーブルの結合条件が一致したレコードを取得する。片方のテーブルのみにしかデータが無い場合には含まれない:

`売上`テーブル

| 国 | 年月 | 売上 |
|:-:|:-:|-:|
| 薩摩 | 21/6 | 12 |
| 薩摩 | 21/7 | 13 |
| 大隅 | 21/6 | 22 |
| 大隅 | 21/7 | 23 |
| 日向 | 21/6 | 32 |
| 日向 | 21/7 | 33 |

`四半期`テーブル

| 年月 | 四半期 |
|:-:|:-:|
| 21/5 | 1Q |
| 21/6 | 1Q |

↓ 内部結合

| 国 | 年月 | 売上 | 四半期 |
|:-:|:-:|-:|:-:|
| 薩摩 | 21/6 | 12 | 1Q |
| 大隅 | 21/6 | 22 | 1Q |
| 日向 | 21/6 | 32 | 1Q |

SQLの記載方法:

```SQL
SELECT
    国, 年月, 売上, 四半期
FROM
    売上 INNER JOIN 四半期
    ON 売上.年月 = 四半期.年月
        
;
```
### 左外部結合
最初のテーブルの全レコードおよび二つのテーブルの結合条件が一致したレコードを取得する:

`売上`テーブル

| 国 | 年月 | 売上 |
|:-:|:-:|-:|
| 薩摩 | 21/6 | 12 |
| 薩摩 | 21/7 | 13 |
| 大隅 | 21/6 | 22 |
| 大隅 | 21/7 | 23 |
| 日向 | 21/6 | 32 |
| 日向 | 21/7 | 33 |

`四半期`テーブル

| 年月 | 四半期 |
|:-:|:-:|
| 21/5 | 1Q |
| 21/6 | 1Q |

↓ 左外部結合

| 国 | 年月 | 売上 | 四半期 |
|:-:|:-:|-:|:-:|
| 薩摩 | 21/6 | 12 | 1Q |
| 薩摩 | 21/7 | 13 | null |
| 大隅 | 21/6 | 22 | 1Q |
| 大隅 | 21/7 | 23 | null |
| 日向 | 21/6 | 32 | 1Q |
| 日向 | 21/7 | 33 | null |

SQLの記載方法:

```SQL
SELECT
    国, 年月, 売上, 四半期
FROM
    売上 LEFT OUTER JOIN 四半期
    ON 売上.年月 = 四半期.年月
        
;
```

# `ORDER BY`句
- 並べ替えのキーを指定（昇順）
- 降順にするには、`列名 DESC`
- 複数キーで並べ替えるには、`,`（カンマ）で区切る

# ネストした`SELECT`文
`SELECT`文の中で入れ子で`SELECT`文を呼び出すことが可能。入れ子状の`SELECT`文のことを、「ネストした`SELECT`文」、「副問い合わせ」等という。

## `FROM`区内の`SELECT`文
（後日追記）
## `WHERE`区内の`SELECT`文
以下のテーブルで、四半期の値が2Qのものだけ取り出したい場合

| 国 | 年月 | 売上 |
|:-:|:-:|-:|
| 薩摩 | 21/6 | 12 |
| 薩摩 | 21/7 | 13 |
| 大隅 | 21/6 | 22 |
| 大隅 | 21/7 | 23 |
| 日向 | 21/6 | 32 |
| 日向 | 21/7 | 33 |

`四半期`テーブル

| 年月 | 四半期 |
|:-:|:-:|
| 21/5 | 1Q |
| 21/6 | 1Q |
| 21/7 | 2Q |

↓ 四半期の値が2Qのものだけ取り出す

| 国 | 年月 | 売上 | 
|:-:|:-:|-:|
| 薩摩 | 21/7 | 13 | 
| 大隅 | 21/7 | 23 | 
| 日向 | 21/7 | 33 | 

ネストしない場合は、結合した上で`WHERE`で指定する:

```SQL
SELECT
    国, 年月, 売上
FROM
    売上 LEFT OUTER JOIN 四半期
    ON 売上.年月 = 四半期.年月
WHERE
    四半期.四半期 = "2Q"
;
```

ネストする場合は、結合が不要:

```SQL
SELECT
    国, 年月, 売上
FROM
    売上 
WHERE
    年月 IN (
        SELECT
            年月
        FROM
            四半期
        WHERE
            四半期 = "2Q"
    )
; 
```

どちらかでしかできないことがあればそれを使い、そうでなければ読みやすい方を使えばよい。

# `UNION`文による統合
`UNION`または`UNION ALL`文を使えば、2つの`SELECT`文の結果を統合（＝行を追加）できる。

`売上1`テーブル

| 国 | 年月 | 売上 |
|:-:|:-:|-:|
| 薩摩 | 21/6 | 12 |
| 薩摩 | 21/7 | 13 |
| 大隅 | 21/6 | 22 |

`売上2`テーブル

| 国 | 年月 | 売上 |
|:-:|:-:|-:|
| 薩摩 | 21/6 | 12 |
| 薩摩 | 21/7 | 13 |
| 大隅 | 21/6 | 22 |

↓`UNION`または`UNION ALL`

| 国 | 年月 | 売上 |
|:-:|:-:|-:|
| 薩摩 | 21/6 | 12 |
| 薩摩 | 21/7 | 13 |
| 大隅 | 21/6 | 22 |
| 大隅 | 21/7 | 23 |
| 日向 | 21/6 | 32 |
| 日向 | 21/7 | 33 |

上記のSQL文は以下のとおり:


```SQL
SELECT
    国, 年月, 売上
FROM
    売上1 
UNION  #またはUNION ALL
SELECT
    国, 年月, 売上
FROM
    売上2
;
```

制約:

- 2つの`SELECT`文の結果の列数が同じでなければ使用不可
- 2つの`SELECT`文の結果の各列の型が同じか、2つめの`SELECT`文の結果の各列が1つめの`SELECT`文の結果の各列に変換可能でなければ使用不可
- 列名は1つめの`SELECT`文の結果のものが使われる

`UNION`と`UINION ALL`の違い:

- `UNION`: 統合したデータ内に重複があれば削除。レコード数が多い場合、サーバーへの負荷が大きい。
- `UNION ALL`: 統合したデータ内に重複があっても削除しない。

# その他
SQLはDBに依って記述方法が微妙に異なることがある。例えば、

- `;`（セミコロン）の要否。とりあえず付けておけば問題ない。
- `AS`の利用可否