【オンラインハンズオン】はじめてのSQL 勉強会レポート

# この記事について
この記事は、2021年4月10日に[どいこさん](https://twitter.com/doiko_niki)が主催した[【オンラインハンズオン】はじめてのSQL](https://x-hack.connpass.com/event/208253/)のレポート記事です。

SQL勉強会で扱った内容を、どいこさんがQiitaにまとめてくださっております！
とても説明が分かりやすく、また、**「間違ったSQL文を正しく直す」** **「問題文を読み、その処理を行うSQLを自分で考えてみる」** といった練習問題もありますので、楽しみながらSQLの知識を定着させる事のできるものになっております！
https://qiita.com/doiko/items/0b4c5353dccd8497b0a8

## そもそもSQLとは
>データの操作や定義を行うためのデータベース言語

[wikipediaより引用](https://ja.wikipedia.org/wiki/SQL)

データベースに接続、プログラムを通じてデータベース内のデータを操作する時に、Java、Ruby、PHP、etc...といった他のプログラミング言語と併用される。

## データベース言語とは
>コンピュータのデータベースを扱う為の言語

[wikipediaより引用](https://ja.wikipedia.org/wiki/%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E8%A8%80%E8%AA%9E)


データベース言語には、いくつか種類があって

- **データベース内にある情報を、取得・追加・編集・削除する為の、データ操作言語**

- **新しくデータベースやテーブルを作成する、または今あるデータベースやテーブルを削除・編集する為のデータ定義言語**

- **データベースにアクセスできる人を制御する為のデータ制御言語**

この三つの種類のデータベース言語があります。

 

[こちらのサイトを参考にしました](https://ja.wikipedia.org/wiki/%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E8%A8%80%E8%AA%9E)

## データベースとは
>ある特定の条件に当てはまるデータを複数集めて、後で使いやすい形に整理した情報の塊の事

[こちらのサイトから引用しました](https://proengineer.internous.co.jp/content/columnfeature/6411)

ざっくりと説明すると、**データを保管して管理する為のExcelのような物**

![データベース内のテーブル](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/933264/1402a9ee-31b4-53f8-03da-a1a28d35ed36.png)

[データベースを扱う時に使われる用語]

- テーブル・・・**データを格納する表のような物(Excelでいうシートのようなもの)**

- カラム・・・**テーブルの列(項目にあたるもの)**

- レコード・・・**テーブルの行(1件分のデータにあたるもの)**

- フィールド・・・**レコードを構成する一つ一つの要素の事(Excelでいうセルに当たる部分)**

## SQLを書いてみよう！
今回の勉強会では、こちらのWEBブラウザ上でSQLを実行出来るツールを使用しました

https://sqliteonline.com/

また、SQLはPostgresqlを使用しました

## SQLの基本ルール
SQLにはいくつかのルールがあって

### 改行・スペース

- SQL文の途中で、改行する事が出来る

- 行の先頭や、途中に半角スペースを入れる事が出来る

```PostgreSQL:SELECT文
SELECT * FROM menus WHERE id = 2;

#改行無しのSELECT文

SELECT * FROM menus
 WHERE id = 2; 

#改行ありのSELECT文
#SQL文が長くなった時に、見やすく書く事が出来る
```

### コメント
そのSQL文についてのコメントを残す事が出来る

- ハイフン(-)を二つ続けて書くと、ハイフンの後ろから行の最後までをコメントとする事が出来る

- /* から */までをコメントとする事が出来る。複数行のコメントを書く時に使う

```Postgresql:SQL文
-- ハイフンを使うと、2つから行の最後までがコメントとなる

/* 
/*から、*/までがコメントととなる。この書き方を使うと複数行のコメントが書ける
*/
```

### 予約語

予約語とは
ざっくりと説明すると**プログラミング言語や、データベース言語において既に役割の決められている文字列の事です**
SQLでいう、SELECT・UPDATE・DELETEなどの命令に使う文字列が予約語にあたり、テーブル名やカラム名に使う事が出来ない
(どれが予約語で、どれがテーブル名・カラム名か分からなくなる為)

予約語を書く際は、大文字でも小文字でも動作します。

```Postgresql:SELECT文
SELECT * FROM menus WHERE id = 2;

# SELECT、WHEREといった予約語を大文字で書いた場合

select * from menus where id = 2;

# 小文字で書いた場合。小文字で書いても動作する
```

## テーブルの作成 (CREATE TABLE文)
CREATE TABLE文は、テーブルの名前や、どんなデータを入れるカラムなのかを定義する為のデータ定義言語
**データベース内に、データを格納する為のテーブルを作成する為のSQL文**


```Postgresql:create文
CREATE TABLE menus
   ("id" int, "category" varchar(8), "name" varchar(20), "price" int)
;

#CREATE文の書き方
#CREATE TABLE テーブル名(
    カラム名 データ型
)

#カラムが複数ある場合は、カンマ(,)で区切る
```

>Postgresqlでは、テーブル名やフィールド名を「"」（ダブルクォート）で括ることにより、特殊文字をテーブル名やフィールド名に含めることが出来ます。
また、ダブルクォートを用いて作成したテーブル名は SELECT 等で使用する際にもダブルクォートで括って指定する必要があります。
https://koumei2.com/wiki/1183#:~:text=PostgreSQL%20%E3%81%A7%E3%81%AF%E3%80%81CREATE%20TABLE%20%E6%96%87,%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B%E5%BF%85%E8%A6%81%E3%81%8C%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99 より

##テーブルにデータを追加する (INSERT文)

```Postgresql:insert文
INSERT INTO menus
   ("id", "category", "name", "price")
VALUES
   (1, '定食', '焼きそば定食', 680),
   (2, '定食', 'ハンバーグ定食', 680),
   (3, '定食', 'ミックスフライ定食', 800),
   (4, '単品', 'カレーライス', 600),
   (5, '単品', 'オムライス', 800),
   (6, '単品', 'シーザーサラダ', 580),
   (7, 'デザート', 'ケーキ', 480),
   (8, 'デザート', 'プリンパフェ', 600),
   (9, 'ドリンク', '生ビール', 380),
   (10, 'ドリンク', '焼酎', 500),
   (11, 'ドリンク', 'コーヒー', 300)
;

#insert文の書き方
#INSERT INTO テーブル名
   (カラム名)
VALUES
   (値)
;

#カラム名、値が複数ある場合はカンマ(,)で区切る
#文字列はクォーテーションで囲う事で文字列に、数値は何も囲わない事で数値として扱われる
```

INSERT文を使う時、PostgreSQLなどの標準SQL(MySQL以外)では

- シングルクォーテーションで囲うと文字列

- ダブルクォーテーションで囲うとカラム名

MySQLでは

- シングル・ダブルクォーテーションで囲うと文字列

- バッククォートで囲うとカラム名

として、扱われます

[こちらのqiitaを参考にしました](https://qiita.com/Sirloin/items/b14ab34415a1ebd8a9c5)

## データを検索・取得する (SELECT文)

```PostgreSQL:SELECT文
# SELECT文の書き方
SELECT カラム名 FROM テーブル名

# 複数のカラムを指定する事も出来る。その際はカラム名をカンマで区切る
SELECT カラム名1, カラム名2 FROM テーブル名

# カラム名の所を*とすると、全てのカラムの値が取得出来る
SELECT * FROM テーブル名
```

### カラムに別名をつける (AS句)
AS句というのを使うと、カラム名・テーブル名に別名(エイリアス)をつける事が出来る

```PostgreSQL:AS句
SELECT name AS メニュー名, price AS 価格
  FROM menus AS メニュー

# ASの使い方
SELECT カラム名 AS なんて名前にしたいか
   FROM テーブル名 AS なんて名前にしたいか

# ASは省略する事も出来る
SELECT カラム名 なんて名前にしたいか
   FROM テーブル名 なんて名前にしたいか
```

#### どんな時に別名をつけるか
複数のテーブルがデータベースの中にある場合に、**どのテーブルのカラムなのかを分かりやすくしたい時(SQLの見やすさを意識したい時)**
[こちらのサイトを参考にしました](https://sql-oracle.com/sqlserver/?p=1201#:~:text=%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%8C%E8%A4%87%E6%95%B0%E3%81%82%E3%81%A3%E3%81%A6,%E3%80%8CAS%E3%80%8D%E3%82%92%E4%BD%BF%E3%81%84%E3%81%BE%E3%81%99%E3%80%82)

##テーブル内のデータを編集する (UPDATE文)

```PostgreSQL:UPDATE文
# UPDATE文の書き方
UPDATE テーブル名
  SET カラム名 = どの値に更新するか
WHERE どの行を更新するかの条件 (条件を指定しないと全ての行の指定したカラムの値が編集される)

# 例文
UPDATE menus
 SET price = 100
WHERE name = '生ビール'

# どの値に編集するのかは複数指定する事が出来る。その際はカンマ(,)で区切る
UPDATE menus
 SET price = 100, name = '生中'
WHERE name = '生ビール'
```

## テーブル内のデータを削除する (DELETE文)

```PostgreSQL:DELETE文
# DELETE文の書き方
DELETE FROM テーブル名
  WHERE どこの行を削除するかの条件 (条件を指定しないと全ての行が削除される)
```
DELETE文は特定のカラムではなく、特定の行を削除するSQL文なのでカラムの指定をする必要は無し

またここまでに出てきたSQL文は、システムに必要な

- **データの作成(CREATE)**

- **読み取り(READ)**

- **更新(UPDATE)**

- **削除(DELETE)**

の処理を行うSQL文。それぞれのSQL文の頭文字を取り、**CRUD（クラッド)**と呼ぶ

## 条件の設定 WHERE句

WHERE句を使う事で、取得するデータの条件の設定をし、データを絞り込む事が出来る
INSERT文以外のSQL文(SELECT、UPDATE、DELETE)で使用する事が出来る
またWHERE句を使って条件を絞り込む時は、必ず**正しい条件を書いて絞り込む事**

### どんな条件を書いたらいいの？

結果が必ずTRUEかFALSE(真か偽)になる条件式を書く

```PostgreSQL:SELECT文
#例

SELECT FROM * menus
   WHERE name = '唐揚げ'
# 名前カラムの値が唐揚げと同じ物

SELECT * FROM menus
  WHERE price > 600
# 値段カラムの値が600円よりも大きい物
```
記号(演算子)の種類と意味

| 記号(演算子) | 意味 |
|:-:|:-:|
| = | 左右の値が等しい  |
| < | 左辺は右辺より小さい  |
| >  | 左辺は右辺より大きい  |
| <=  |  左辺は右辺の値以下 |
| >=  | 左辺は右辺の値以上  |
| <>  | 左右の値は等しくない  |

## NULLとは
そこに何も格納されていない、未定義の値

###どんな時に使うのか

たとえばユーザー登録時の住所の情報を格納するテーブル「addresses」があったとして
住所の登録には必ず、郵便番号や住んでいる都道府県の入力が必要になる為、「郵便番号」や「都道府県」の列は必ず値が存在するはずですが、
一戸建てに住んでいる方の場合だとマンションに住んでいるわけではないので、「マンション名・部屋番号」の列には一戸建てのユーザーの場合格納できるデータがないかと思います。
そんな場合にNULLとしたり、空文字（' '）を格納したりします。

また、このあたりはどういう仕様にするのかによって変わって来るそうです



### カラム内の値がNULLかどうかの判別の仕方

WHERE句を使うのは、他のと変わらないけれども

```PostgreSQL:SELECT文
SELECT * FROM menus
   WHERE name = NULL
#　この書き方では、判別出来ない

SELECT * FROM menus
  WHERE name IS NULL
# =ではなく、IS NULLを使う事で判別が出来る

SELECT * FROM menus 
  WHERE name IS NOT NULL 
# NULLではない値を取得する時はIS NOT NULLを使う
```

なぜ、カラム名 = NULLでは判別できないのか

＝や>といった演算子は、値同士を判別する為のものであり、NULLは値ではない為

###指定した範囲のデータを取得する (BETWEEN演算子)

例えば値段が〇〇以上〇〇以下のものといったように、値の上限値と下限値を指定する事で、値がその範囲内に収まっているデータを取得することができる

```PostgreSQL:SELECT文
# BETWEENの書き方
SELECT * FROM menus
   WHERE カラム名 BETWEEN 下限値　AND 上限値
```

##複数の値の指定 IN演算子

カラムの値が指定した値のリストの中に一致するものがあるかどうか調べる事が出来る
＝だと1つの値との比較しかできないけど、INならたくさんの値との比較が出来る

```PostgreSQL:SELECT文
# INの書き方
SELECT * FROM menus 
   WHERE カラム名 IN(値1,値2)
# ()カッコ内の値は、カンマ(,)で区切る

SELECT * FROM menus
   WHERE category IN('定食','単品')
# カテゴリーが定食、単品の行を取得するという意味

# NOT INを使うと、リスト内の値以外のものがあるかどうかを調べることが出来る
SELECT * FROM menus
   WHERE category  NOT IN('定食','単品')
# カテゴリーが定食、単品以外の行を取得するという意味
```

カラムの中の値がNULLの場合はNULLを返す

##パターンマッチングを行う (LIKE演算子)

今までに出てきた演算子は、特定の単語と完全に一致しているかをチェックしていたが、LIKE演算子を使うと完全一致ではなく「特定の単語を含んでいるか」というチェックの仕方が出来る
また、LIKE演算子には%と_という記号も一緒に使う

| 記号 | 意味 |
|:-:|:-:|
| %  | どれでもいい0文字以上の文字列  |
| _  | どれでもいい一文字  |

```PostgreSQL:SELECT文
SELECT * FROM menus
   WHERE name LIKE '単語と記号'

# 例えば
SELECT * FROM menus 
  WHERE name LIKE '%定食'
# %は0文字以上の文字列として扱う事が出来るので、〇〇定食という名前のものはあるかをチェックしている

SELECT * FROM menus
  WHERE name LIKE '焼き%'
# この場合は、焼きの後ろに0文字以上の文字列はあるかという意味なので、焼き〇〇という名前のものはあるかをチェックしている
```

## 論理演算子　(AND、OR、NOT)
### AND
AかつB　（2つの条件式両方に一致する場合のみTRUEとなる）

```PostgreSQL:SELECT文
条件式A AND 条件式B
```

```PostgreSQL:UPDATE文
UPDATE menus
SET price = 600
WHERE category = 'ドリンク'
AND price > 300
# カテゴリーがドリンクかつ値段が300円以上の物の価格を600円にするという意味
```
### OR
AまたはB　（2つの条件式のどちらかに一致するならTRUEとなる）

```PostgreSQL:SELECT文
条件式A AND 条件式B
```

```PostgreSQL:SELECT文
SELECT name, price FROM menus
WHERE name = '生ビール'
OR name = '焼酎
# 名前が生ビールまたは、焼酎の物の値段を取得するという意味
``` 

### NOT 
条件式に当てはまらない場合にTRUEとする

```PostgreSQL:SELECT文
SELECT name, price FROM menus
WHERE NOT name LIKE ('%サラダ%')
#名前がサラダ以外の物の名前と価格を取得するという意味
```

### 論理演算子の注意点
**複数の論理演算子を使ったSELECT文を実行する時は、前から順番にではなく
優先順位の高い演算子から実行される**

例えば、定食または単品で、nameにライスまたはフライとつくメニューを抽出する場合

```PostgreSQL:SELECT文
SELECT category, name, price FROM menus
WHERE category = '定食' or category = '単品' 
AND name LIKE ('%ライス%') OR name LIKE ('%フライ%')
```
このSELECT文だと、うまく抽出が出来ない

なぜなら、 論理演算子の実行順番は

1.NOT
2.AND
3.OR

となるので、まずANDが実行されて「単品かつ名前にライスを含むもの」が抽出された後
「定食」OR 「単品かつ名前にライスを含むもの」OR 「名前にフライを含むもの」の結果が返る為

### どうすれば、狙い通りの抽出出来るか

```PostgreSQL:SELECT文
SELECT category, name, price FROM menus
WHERE ( category = '定食' or category = '単品' ) 
AND ( name LIKE ('%ライス%') OR name LIKE ('%フライ%'))
```

上記のようにカッコ()を使い、優先順位をあげると狙い通り抽出出来ます。



以上になります！ご拝読頂き有り難うございました!