SQLを初めて勉強した時の備忘録

#はじめに
今回は自分が初めてSQLを勉強した時の備忘録になっています。

お付き合い頂ければ幸いです。

##SQLとは
SQLとはデータベースを操作するときに使われる言語です。

特に、リレーショナルデータベース(RDB)を操作するときに使われます。

リレーショナルデータベース(RDB)について解説します。

###リレーショナルデータベース(RDB)とは
データベースの内、関連のあるデータがまとめられているデータベースのことです。

どのようにデータがまとめられているかというと、データが表形式にまとめられています。

有名な`Oracle`や`MySQL`はリレーショナルデータベースのひとつです。

###RDBの構成
RDBはExcelのような表形式のデータになっています。

表のことを`テーブル`といい、また縦の列のことを`カラム`、横の列のことを`レコード`と呼びます。

また、データベースに送る命令を`クエリ`といいます。

SQLはその`クエリ`を書くための言語だということができます。

##SQLのコマンド
それではSQLのコマンドについてまとめていきます。

###SELECT、FROM
table1というテーブルからnumberというカラムを指定するときには以下のようにします。

```.sql
SELECT number
FROM table1;
```

どこまでがクエリなのかを伝えるために、クエリの最後には;(セミコロン)を打ちます。

複数のカラムを指定したいときには以下のようにします。numberカラムとpriceカラムを指定しましょう。

```.sql
SELECT number, price
FROM table1;
```

全てのカラムのデータを取得する場合は、greedy match(欲張りマッチ)である*を使いましょう。

```.sql
SELECT *
FROM table1;
```

###WHERE
WHEREを用いると、特定のデータを取得することができます。

`WHERE カラム名=○○`とすることで、カラム名が○○であるデータを取得することができます。

以下のようにすると、table1テーブルのnameカラムが犬であるレコードを取得することができます。

```.sql
SELECT *
FROM table1
WHERE name="犬";
```

データベースのデータ型には大きく分けて`「数値型」「文字列型」「日付型」`が存在し、文字列型と日付型はダブルクォーテーションで囲む必要があります。データ型については[こちら](https://qiita.com/hayabusa3703/items/d3ebf2dcd6257cb5cf1e)の記事を参考にしてください。

また、WHEREの後ろにNOTを用いると、NOTの後ろの条件を満たさないデータを取得できます。


```.sql
SELECT *
FROM table1
WHERE NOT name="犬";

```



また、WHEREの後ろには比較演算子を用いて条件を指定することもできます。次のようにすれば、table1テーブルにおいてpriceカラムのデータが500以上のレコードを取得することができます。


```.sql
SELECT *
FROM table1
WHERE price >= 500;
```


#### LIKE
また、WHEREの後にLIKE演算子を用いることで、「～のようなデータ」を取得することができます。

LIKE演算子は、どの文字列ともマッチするワイルドカードとともに使います。LIKE演算子は、「%」をワイルドカードとして扱います。

nameカラムのデータに「柴犬」「秋田犬」「土佐犬」のデータが含まれていて、この三つの「~犬」のデータを含むレコードを取得したい時は、以下のようにします。


```.sql
SELECT *
FROM table1
WHERE name LIKE "%犬";
```

このワイルドカードを用いた文字列の指定は正規表現とほぼ同じなので、解説は省略します。

###NULLについての取り扱い
データベースは完璧ではないため、「データが入っていないデータ」が存在します。

その「データが入っていないデータ」には`NULL`が入っています。

データが入っていないデータを指定するときに、=を用いても取得できません。

例えば、table1テーブルのnameカラムがNULLであるデータを取得するとき、以下のようにすると失敗します。

```.sql
SELECT *
FROM name
WHERE name=NULL;
```

NULLを指定したい時は`is NULL`や`is NOT NULL`を使用するようにしましょう。
nameがNULLであるレコードを取得するときは以下のようにします。

```.sql
SELECT *
FROM name 
WHERE name is NULL;
```
nameがNULLでないレコードを取得するときは以下のようにします。

```.sql
SELECT *
FEOM name 
WHERE name is NOT NULL;
```

###AND演算子とOR演算子
WHEREで複数の条件を指定するときは、AND演算子とOR演算子を使います。

ANDは`～かつ～`のように条件を指定でき、ORは`～または～`のように条件を指定できます。

nameが秋田犬かつ、priceが5000以上のレコードを取得するには以下のようにします。

```.sql
SELECT *
FROM table1
WHERE name="秋田犬"
AND price >= 5000;
```

nameが秋田犬または土佐犬であるレコードを取得するには以下のようにします。

```.sql
SELECT *
FROM table1
WHERE name="秋田犬"
OR name="土佐犬";
```

###データの並べ替えについて
データを並べ替えるには、`ORDER BY`を用いましょう。

昇順(小さい方から大きい方)に並べ替えたい場合は`ASC`を用いて、降順(大きい方から小さい方)に並べ替えたい場合は`DESC`を用います。

`ORDER BY カラム名　ASC`のような構文で使います。

table1テーブルのデータをpriceカラムについて昇順(ASC)に並べ替えたい場合は、以下のようにします。

```.sql
SELECT *
FROM table1
ORDER BY price ASC;
```


###必要な数だけデータを取得したいとき
データを取得する数を制限するときは`LIMIT`を用います。

table1テーブルのデータをpriceカラムについて昇順(ASC)に並べ替え、最大10件を表示する場合は以下のようにします。

```.sql
SELECT *
FROM table1
ORDER BY price ASC
LIMIT 10;
```

###重複したデータの省き方
`DISTINCT`を用いれば、検索結果から重複するデータを除くことができます。

使い方としては`DISTINCT(カラム名)`とすることで、指定したカラムに重複したデータが含まれる場合にそのレコードを除くことができます。

table1テーブルのnameカラムに重複する要素がある場合に、その重複要素を除去したい場合は以下のようにします。

```.sql
SELECT DISTINCT(name)
FROM table1;
```

###四則演算を使う
sqlでは、四則演算を行うことも可能です。

以下のようにすれば、priceカラムを1.10倍したデータを取得できます。

```.sql
SELECT name, price*1.10
FROM table1;
```

###数値の合計の取得
`SUM関数`を用いることで、数値の合計を取得することができます。
以下のようにすると、table1テーブルのpriceカラムの豪渓を取得できます。

```.sql
SELECT SUM(price)
FROM table1;
```

SUM関数はWHEREと併用することも可能です。
以下のようにすると、table1テーブルのnameカラムが「秋田犬」であるレコードのpriceの合計を取得できます。

```.sql
SELECT SUM(price)
FROM table1
WHERE name="秋田犬";
```

###数値の平均の取得
`AVG関数`を用いることで、数値の平均を取得できます。
以下のようにすると、table1テーブルのnameカラムが「秋田犬」であるレコードのpriceの平均を取得できます。

```.sql
SELECT AVG(price)
FROM table1
WHERE name="秋田犬";
```

###データの数を計算する
データの数を計算するには、`COUNT関数`を使います。

`COUNT(カラム名)`という構文で用いれば、そのカラムに含まれるデータの合計が取得できますが、そのカラムにNULLがある場合はNULLを省いた合計を取得します。

単純にレコードの合計を取得したい場合は`COUNT(*)`としましょう。

以下のようにすれば、table1テーブルに含まれるレコードの合計を取得できます。

```.sql
SELECT COUNT(*)
FROM table1;
```

また、以下のようにすればnameカラムが「秋田犬」であるレコードの数を取得できます。

```.sql
SELECT COUNt(*)
FROM table1
WHERE name="秋田犬";
```

###最大と最小のデータの取得
`MAX関数`を使えば最大のデータを、`MIN関数`を使えば最小のデータを取得できます。

以下のようにすれば、nameカラムが「秋田犬」であるレコードの中でpriceカラムが最小のもののpriceの値を取得できます。

```.sql
SELECT MAX(price)
FROM table1
WHERE name="秋田犬";
```

###グループ化について
`GROUP BY`を用いると要素をグループ化することができます。

このグループ化の部分は少し複雑なので、詳しく見たい人は[こちら](https://taityo-diary.hatenablog.jp/entry/2018/06/09/080903)の記事を参考にしてください。

`GROUP BY カラム名`の構文で、「カラムに重複している要素」でグループ化することができます。

例えば、table1テーブルのnameカラムに「秋田犬」「土佐犬」「柴犬」の三要素のみが存在する場合を考えます。

この場合に`GROUP BY name`とすると、「秋田犬」のグループと「土佐犬」のグループと「柴犬」のグループの三つのグループに分割されます。

この三つのグループに分割された状態で、もともとのpriceカラムにアクセスすることはできません。

なぜなら、三つのグループに分割された段階で、「グループに対するデータ」しか残っていないからです。

具体的には、「秋田犬」のグループの合計のpriceや、「土佐犬」のグループの要素の数の合計などのデータにしかアクセスできなくなります。

以下のように覚えてください。

`GROUP BYを用いた場合にSELECTで使えるのは、GROUP BYで指定しているカラム名と、集計関数のみである。`

集計関数(SUMやCOUNTなど)であるならば、そのグループに対してのデータにアクセスできますよね。

以下のようにすると、table1テーブルをnameカラムについてグループ化し、そのグループ化したデータについてのpriceの合計とグループ化したデータの数にアクセスできます。

```.sql
SELECT name, SUM(price), COUNT(*)
FROM table1
GROUP BY name;
```

上のコードにより、グループ化されたnameカラムの、各々のグループに対するpriceカラムの値の合計と要素の合計をnameカラムのグループに対して表示することができます。

###WHEREとGROUP BYを同時に使う場合
WHEREとGROUP BYを同時に使うと、先にWHEREで条件が絞られた後にGROUP BYでグループ化されます。

例えば以下のようにすると、table1テーブルでpriceが5000以上のデータに対して、nameカラムをグループ化した後にそのグループ化したデータに対してpriceの合計とデータの数を取得することができます。

```.sql
SELECT name, SUM(price), COUNT(*)
FROM table1
WHERE price >= 5000
GROUP BY name;
```

###グループ化したデータを絞り込むHAVINGについて
WHEREはグループ化する前に条件を絞り込むことができますが、HAVINGを用いるとグループ化した後に条件を絞り込むことができます。

`FROM → WHERE → GROUPBY → HAVING → SELECT → ORDERBY`の順番にsqlのコマンドが実行されることを覚えておきましょう。

WHEREとHAVINGの違いについては[こちら](https://dev.classmethod.jp/server-side/db/difference-where-and-having/)の記事に良くまとめられていたので、参考にしてください。

HAVINGはグループ化した後のデータに対して用いるため、HAVINGの後の条件に指定できるのは`GROUP BYで指定しているカラム名と、集計関数のみである`という点に注意してください。

以下のようにすると、table1テーブルのデータをnameカラムについてグループ化した後に、そのグループ化したデータのpriceの合計が50000以上のデータを取得できます。

```.sql
SELECT name, SUM(price)
FROM table1
GROUP BY name
HAVING SUM(price)>=50000;
```

#終わりに
今回はここまでになります。ここまでお付き合い頂きありがとうございました。