SQL 超初心者向け スッキリわかるSQL 3章（操作する行の絞り込み）

# はじめに
さて今回もやってきます！スッキリわかるSQL3章です！
本記事もこのテーブルを題材としています。
![スクリーンショット 2021-07-22 18.55.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1183228/d3564be4-9ab3-b2af-e443-bbbb598524e7.png)

# WHERE句による絞り込み
修飾語で最もよく使われるのが、WHERE句です。
いい方変えると、WHERE句を自由自在に使えれば、データを自由自在に操作できちゃう！ってことです。
それだけ大事ってこと。

### ではWHERE句の基本をここでおさらい。
`①WHEREを指定しないと「すべての行」が処理対象となる。`
`②SELECT、UPDATE、DELETE文で使用可能(INSERT文では使用できない）`
`③WHEREの後ろには条件式を記述。`

ふぅ〜書くと頭の中がすっきりしますね！

### WHERE句の基本構文
`WHEREの後ろにはどんな式でも良いというわけではない`

具体例をやっていきましょ。
基本の形： WHERE 条件式

```
OKな例 WHERE 出金額 < 10000
NGな例 WHERE 出金額 + 10000 ←後ろに書くことができるのは条件式のみ
```
`NGな理由は、WHEREの後ろに書くことができるのは条件式のみだからです。`

#### いやそもそも条件式ってなんだよ！

条件式とは
➩`結果が必ず真か偽になる式のこと`

RubyのIF文も条件式だね。あれをイメージできれば楽勝です。
簡単にいうとWHEREの後ろに記載する条件式は、
`YESかNOかで判断できる式じゃないといけませんよ！って言ってます。`

なのでWHERE 出金額 + 10000はNGなんです。

でもここでさらに疑問が、どうして真か偽になる式にしか書いちゃだめなんだ？？
➩これはDBMSの気持ちのなればわかるみたいです。
下記のようなSQL文を発行したとすると、、、

```
DELETE FROM 家計簿
       WHERE 出金額 > 0
```

DBMSは、この行（レコード）は真、この行は偽ってひとつずつチェックしているんですね。
なので条件指示してあげないとDBMSが困ってしまう。
「真か偽になる式」しか書いちゃダメなんです。

### 条件式で使う比較演算子なにがあるの？

条件式は ＝や＜のような記号を含んだ式になることがほとんど。これらは`比較演算子`という。

### 主な比較演算子

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

Rubyで学んだ比較演算子と同じなので頭に入ってきます！

## NULLの判定
NULLとは
➩何も格納されていない。
➩数字のゼロや空白文字とも異なる

### 何も格納されていない状態を判定するには、 =、><の演算子が利用できない
ん？どういうこと？
では具体的に見ていきます。
まずNGのNULLの書き方

```
※NGの書き方
SELECT * 
  FROM 家計簿
WHERE 出金額 = NULL ← これだと正しく判定されない
```

ではどうやればいいか？
### NULLの判定
列の値がNULLであることを判定する
`式 IS NULL `
NULLでないことを判定する
`式 IS NOT NULL `

```
SELECT * 
  FROM 家計簿
WHERE 出金額 IS NULL
```

これでNULLの判定ができたー！
ん？またしても疑問が。
#### なんで比較演算子の＝でNULLかどうかを判定しちゃいけないんだ？
➩この理由は、比較演算子はもともと値と値を比較するためのものだから。
なので、「値ではないとNULL」を比較すると、不明な結果になりUNKNOWN（不明、計算不能）になる。

#### UNKNOWNとは？
UNKNOWNは３値論理とよばれるしくみのひとつ。
条件式の結果は、真(true）か(false)になるけど、もうひとつUNKNOWN（不明、計算不能）がある。

## 各演算子
いや〜結構勉強しましたね！ 次はさまざまな演算子について勉強しよう！
比較演算子ではなく演算子。詳細は書かずに、要点だけまとめてきます。

#### LIKE 演算子
文字列があるパターンに合致しているかをチェックすることを`パターンマッチ`という。
このパターンマッチにLIKE演算子を使う。
うん、簡単にいうと部分一致の検索です。◯◯◯という文字列の一部に含むか判定できます。

LIKE演算子に使えるパターン文字

| パターン文字 |  意味 | 
|---:|-------------|
| %  |  任意の0文字以上の文字列   |   
| _  |  任意の1文字   |

１月に関連する行を取得するSELECT文


```
SELECT * 
  FROM 家計簿
WHERE メモ 
LIKE '%1月%' ←1月の前後に任意の0文字以上の文字列がついてもよい
```

#### BETWEEN(ビトゥイーン） 演算子

BETWEEN(ビトゥイーン） 演算子は、ある範囲内に値が収まっているかを判定

100~3000円の出費を取得するSELECT文

```
SELECT * 
  FROM 家計簿
WHERE 出金額 BETWEEN 100 AND 3000 
```
※このあとでてくるANDでも同じ判定が可能みたいです。
状況によるけど BETWEENのほうが処理性能が悪いことがあるらしい。

#### IN/NOT IN 演算子
IN 演算子は値がカッコ内に列挙した複数の値のいずれかに合致するかを判定する演算子。
なるほど。たくさんの値を1度に判定できる強みがあるわけだね。


INをつかった食費・交通費を取得するSELECT文

```
SELECT * 
  FROM 家計簿
WHERE 費目 IN ('食費', '交通費') 
```

NOT INをつかった食費でも交通費でもない行を取得するSELECT文
食費でも交通費でもない行が抽出対象となる！

```
SELECT * 
  FROM 家計簿
WHERE 費目 NOT IN ('食費', '交通費') 
```

### ANY/ALL 演算子
INと少し似た演算子を紹介します。
複数の値と大小を比較したい場合には、ANY演算子やALL演算子
を利用することができます。
  ANYやALLは、`必ずその直前に比較演算子をつけて利用`します
これにより、複数の値との比較を1度に行うことができる。

値リストのそれぞれと比較して、いずれかが真なら真 
式 基本比較演算子 ANY (値1, 値2, 値3・・・)
値リストのそれぞれと比較して、すべて真なら真
式 基本比較演算子 ALL (値1, 値2, 値3・・・)

### AND 演算子とOR 演算子

１つの条件式ではうまく行を絞り込めない場合、`論理演算子`で複数の条件式を組み合わせることが可能

`代表的な論理演算子は ANDとOR演算子がある`
２つの条件式の両方が真の場合だけ、真となる（AかつB）
条件式１ AND 条件式２
２つの条件式のどちらかが真ならば、真となる（AまたはB）
条件式１ OR 条件式２

湊くんの買い物リストテーブル

| カテゴリ |  名称 | 販売店   |価格|
|---:|------|-----|----|---|
| ゲーム  |  スッキリ勇者クエスト | B  | 7140 |  
| ゲーム  |  スッキリ勇者クエスト |Y |6850|
| 書籍  |  魔王討伐日記 |A |1200|
| DVD  |  スッキリわかるマンモスの倒し方 |A |5250|
| DVD  |  スッキリわかるマンモスの倒し方 |B |7140|

２つの条件式を組み合わせたWHERE句
名称がスッキリ勇者クエストかつ販売店Bというもの取得する。

```
UPDATE 湊くんの買い物リスト
   SET 価格 = 6200
WHERE  名称 = 'スッキリ勇者クエスト'
   AND 販売点 = 'B'
```


論理演算子で条件式を組み合わせる際は、演算子が評価される優先順位に注意が必要。
`NOT➩ AND➩ ORの優先順に従って処理される。`

たとえば下記のようなSQLだと意図に反して２行目以外のすべての行が返されてしまう。

```
SELECT * FROM 湊くんの買い物リスト
WHERE  販売店 = 'A'      /* 条件式1 */
   OR  販売店 = 'B'      /* 条件式2 */
   AND カテゴリ = 'ゲーム'  /* 条件式3 */
   OR  カテゴリ = 'DVD'   /* 条件式4 */
```
これは、ORよりもANDの優先順位が高いため、条件式２と３を先に評価し、その結果と条件式１と４をORで評価してしまった。

`こういうときはカッコによる優先順位の引き上げをおこなえば解決する。`

複数の論理演算子を使ったSELECT文

```
WHERE  （販売店 = 'A'      /* 条件式1 */
   OR  販売店 = 'B'）      /* 条件式2 */
   AND （カテゴリ = 'ゲーム'  /* 条件式3 */
   OR  カテゴリ = 'DVD'）   /* 条件式4 */
```
## 主キーとその必要性

突然ですが、下記のようなテーブルあって同じ日付で購入記録のチョコレートを購入が２つある。
これ1行だけ削除したい時どうすればいいでしょうか。

チョコレートの購入が同じ日に2回ある家計簿

| 日付 |  費目 | メモ   |入金額|出金額|
|---:|------|-----|----|---|---|
| 2013-03-03  |  食費 | チョコレートを購入  | 0 | 105 |  
| 2013-03-03  |  食費 |チョコレートを購入 |0|105 | 
| 2013-03-06  |  教育娯楽費 |月刊SQLを購入 |0|1280 | 

上の行だけ消す。下の行だけ消すなど区別できたらいいのですが、実はこれ・・・
`区別できないんです。`
行が区別できないということは、ある特定の行だけを指定して操作することはできないことを意味します.

なので・・`テーブルの中に重複した行（まったく同じ行）が格納されるようなことは絶対に避けるべきです。
ですが今回はテーブルの構造自体に問題があります。`

んじゃ具体的にどうすればいいの？ 答えは `主キー`です。

こういう社員テーブルがあったとして。

| 社員番号 |  年齢 | 性別   |名前|
|---:|------|-----|----|---|
| 2003031  |  45 | 男  | ヨシダシゲル | 
| 2003032  |  45 |男 |ヨシダシゲル | 
| 2003033  |  31 |女 |スガワラタクマ | 


同姓同名かつ年齢や性別の同じ社員がいます。ですがテーブル内に社員番号の行があり、行自体は重複していません。この社員番号が`主キー`となります。主キーは絶対に重複しません。

では上のヨシダシゲルさんだけ主キーをつかって削除してみましょう。

```
DELETE FROM 社員
WHERE 社員番号 = '2003031' /* 社員番号で対象行を特定 */[社員番号]
```

主キーのおかげで社員を特定して削除できましたね！

#### 主キーまとめ
・必ずなにかのデータが格納されている（NULLではない）
・ほかの行と値が重複しない。

## 主キーがないならつくってやろう
情報管理する為に今回みたいな社員テーブルを作ろうとする過程で、
氏名や性別などに加えて社員番号という列を作ることも自然に思いつくこと思います。
このような自然に登場し、主キーの役割を果たせる列を `自然キー`といいます。

一方、家計簿テーブルみたいに「日付」「出金額」「入金額」など思いつくままに列を作っても
主キーの役割を果たせる列が存在しない場合は、主キーを作ればいいだけです。

たとえば家計簿テーブルなら入出金IDみたいな重複しない列を追加する。
このように管理目的の為だけに人為的に追加された列を`人工キー`っていいます。

#### 複数の列で行を識別するやり方もある
`たとえば 氏名、住所、生年月日みたいな３つ列で１つ主キーを構成するやり方`
これを `複合主キー`といいます。

文が長いですね。主キーにもいろいろあるよ！ってことを覚えとけば良さそう。（ええんか）

これで3章の学習は終了です。
いや〜3章長かったですね！お疲れさまです！

## 3章のまとめ
#####WHERE句 
➩WHERE句はSELECT,UPDATE,DELETE文で使うことができる
#####演算子
➩条件式にはさまざまな演算子を記述できる。比較演算子、論理演算子など。
####NULL
➩データが格納されていない。未定義の状態をNULL
➩NULLの判定は IS NULL と IS NOT NULL を使用する。 （＝NULLだと判定されない）
####主キー
➩主キーによって、テーブル内のデータが区別できる
➩主キーは、重複しない値が必ず格納される必要がある

3章ではこの４つを勉強しました！ 
ではまた4章で会いましょう！

# 参考記事
- スッキリわかるSQL入門ドリル