【SQL】制約とは？制約の種類と指定方法

テーブルの各カラムには「制約」と呼ばれるルールを設定することができます。
よく使用される制約と指定方法をまとめてみました。
間違った点があればご指摘いただけますと幸いです。

# SQLの制約とは？
テーブル内に格納できる値に与えるルールのようなもの。
データ入力時に与えたルールと照合して、ルールを満たさないデータは格納できないようにする。
指定する制約の種類によってテーブルに格納できるデータを自由に制御できるようになる。

# 何のために制約するの？
テーブルはデータ型を定義することによって、テーブルに格納するデータの種類を制約できる。
しかし、データ型のみの制約だと、データ型を満たせばテーブル内に意図しない値・無効な値まで入力されてしまうため、誤った操作によって間違ったデータを格納したり削除したりすることも発生しやすくなる。つまり、制約がない場合だと、格納できるデータの種類・精密さに欠けてしまう。
このようなミスを事前に防ぐためにも、SQLではデータ型以外にもテーブルに格納できる値を更に自由に制御するための制約が用意されている。

# 制約の種類
## NOT NULL制約
カラムにNULL値の挿入を禁止する。
NULLは何も入力されていない状態のこと。
そのため、データを格納する際にデータ型を満たす値を何かしら入れないとエラーになる。
入力を必須にしたいカラムに設定される。
```sql
CREATE TABLE users(
    id INT PRIMARY KEY,
    name VARCHAR(30) NOT NULL  /* NOT NULL制約 */
);
```

## DEFAULT制約
カラムの初期値を設定する。
この制約を設定したカラムへのデータ入力を省略した場合、設定された値がカラムに格納される。
カラムに初期値を設定しておきたい場合や予め格納される値が分かっている場合、この制約を設定しておくとデータ入力時のミスを防ぐことができる。
```sql
CREATE TABLE products(
    id INT PRIMARY KEY,
    name VARCHAR(30) NOT NULL
    price INT DEFAULT 1000  /* DEFAULT制約 */
);
```

## 主キー制約（PRIMARY KEY制約）
カラムに主キーを設定する。
主キーとは、テーブル内の１つの行を一意に特定できるカラムのこと。
この制約を設定したカラムに重複したデータ・NULL値のデータを入力しようとするとエラーになる。
```sql
CREATE TABLE products(
    id INT PRIMARY KEY,  /* 主キー制約 */
    name VARCHAR(30) NOT NULL
);
```
主キーとセットで「AUTO_INCREMENT」を設定すると、レコードを追加するたびに自動的に＋1された値がカラムに入力されるようになる
```sql
CREATE TABLE products(
    id INT PRIMARY KEY AUTO_INCREMENT,  /* 主キー制約 + AUTO_INCREMENT */
    name VARCHAR(30) NOT NULL
);
```


## 一意制約（UNIQUE制約）
カラムに重複した値の挿入を禁止する。
主キー(PRIMARY KEY)制約とは異なり、NULL値の入力が可能。
```sql
CREATE TABLE products(
    id INT PRIMARY KEY,
    name VARCHAR(30) UNIQUE  /* 一意制約 */    
);
```

## 外部キー制約（参照/参照整合性/FOREIGN KEY制約）
カラムに外部キーを設定する。
設定した外部キーは参照先カラムに存在する値以外の格納を禁止する。
これにより、両テーブル間のデータ整合性を維持する。
外部キー制約は以下の形で設定する。

**「FOREIGN KEY (参照元のカラム名=外部キー) REFERENCES 参照先テーブル名(参照先のカラム名) ON DELETE (削除時の挙動) ON UPDATE (更新時の挙動)」**

「ON DELETE」以降の部分で、参照先テーブルのカラムの値を削除する際にデータの整合性を維持するための挙動を設定することができる。
更新する際も同様に「ON UPDATE」に続いて設定できる。
```sql
/* 参照先(親)テーブル */
CREATE TABLE departments(
    id INT PRIMARY KEY,
    name VARCHAR(30)   
);

/* 参照元(子)テーブル */
CREATE TABLE employees(
    id INT PRIMARY KEY,
    department_id INT, 
    name VARCHAR(30),
    age INT,
    FOREIGN KEY (department_id) /* 外部キーの設定 */
    REFERENCES departments(id) /* 参照先カラムの設定 */
    ON DELETE RESTRICT /* 参照先のデータ削除時の設定 */
    ON UPDATE RESTRICT /* 参照先のデータ更新時の設定 */
);

```

### 削除規則
参照先テーブルのデータを削除するときの挙動を設定。
デフォルトでは「RESTRICT」が設定されている。

**「ON DELETE RESTRICT」**：削除しようとするとエラーになる。
**「ON DELETE CASCADE」**：参照元テーブルのレコードも削除される。
**「ON DELETE SET NULL」**：参照元テーブルのデータがNULLに置き換わる。
**「ON DELETE NO ACTION」**：「RESTRICT」と同じ。

### 更新規則
参照先テーブルのデータを更新するときの挙動を設定。
こちらも同様に、デフォルトでは「RESTRICT」が設定されている。

**「ON UPDATE RESTRICT」**：更新しようとするとエラーになる。
**「ON UPDATE CASCADE」**：参照元テーブルのレコードも更新される。
**「ON UPDATE SET NULL」**：参照元テーブルのデータがNULLに置き換わる。
**「ON UPDATE NO ACTION」**：「RESTRICT」と同じ。


## 検査制約（CHECK制約）
指定した条件を満たさないデータの格納を禁止する。
例では18歳以上の年齢しか格納できないように条件を指定している。

ここで使用する「CONSTRAINT句」を用いると、制約に名前を付けることができる。
制約に名前をつけると、エラーメッセージが読みやすくなり制約を管理しやすくなる。
ちなみに、名前付き制約は「DROP CONSTRAINT 制約名」で破棄することができる。
```sql
CREATE TABLE users(
    id INT PRIMARY KEY,
    name VARCHAR(30),
    age INT,
    CONSTRAINT age_check CHECK(age >= 18), /* 検査制約の作成 */
);

ALTER TABLE users
    DROP CONSTRAINT check_age;  /* 名前付き検査制約の削除 */
```

# まとめ
制約を正しく設定しておかないと、間違ったデータの格納・削除が発生しやすくなります。
大きなミスを事前に防ぐためにも制約の種類と必要な場面を正しく理解して使いこなしていきたいです。

---
参考サイト
　- [【SQL基礎】制約の種類](https://tech.pjin.jp/blog/2020/10/30/%E3%80%90sql%E5%85%A5%E9%96%80%E3%80%91%E5%88%B6%E7%B4%84%E3%81%AE%E7%A8%AE%E9%A1%9E/)
　- [MySQL　外部キー制約の種類と特徴](https://qiita.com/masapiko/items/72b7f274585dac7ca0da)