テーブル・DB設計するときの極意

# はじめに

<font color="LimeGreen">「テーブル・DBを設計するときのさいきょうの極意」を完全に理解</font>したので
初心者（私）向けに共有する記事です。

どうぞ揉んでいただければ幸いです。対戦よろしくお願いします。

# さいきょうの極意

初心者が「テーブル・DB設計して」と言われると、
「アソシエーションってあったよね・・・バリデーションも？中間テーブルを使うときと使わないときと・・・」と大変に混乱し、何から手をつけていいかわからなくなります。

そんなあなたにこれ！

### ・テーブル・DB設計は「属性」と「関係」の２つだけ
### ・「属性」は必要なものを書くだけ
### ・「関係」は 1:1 / 1:N / N:N しかない（しかも、ほとんど 1:N）

### <font color="LimeGreen">これが極意だ！！！</font>

一般的な、「ユーザーがいて、投稿ができて、コメントといいねができるサービス」で考えてみましょう。
`users` / `posts` / `comments` / `likes` のテーブルが必要そうです。
## 属性：`users`

求められているものを書くだけです。
今回は「id」「ユーザーネーム」「年齢」「email」「パスワード」が必要だとしましょう。
```mermaid
erDiagram
  users {
    int id
    string name
    int age
    string email
    string password
  }
```
はい、完成です。
`int`と`string`は「型」です。例えば「名前」は「数値」ではなく「文字列」です。よって`string`
まあ、型は慣れです。すぐ慣れます。
```mermaid
erDiagram
  users {
    int id PK
    string name "ユーザーネーム"
    int age "年齢"
    string email "email"
    string password "パスワード"
  }
```
親切心でこのように書いてあげてもいいでしょう。未来の自分が喜びます。
`PK`は「プライマリキー」のことで、「一意の識別子」のことです。
難しい言葉ですが、「オリジナルでユニークなid」と覚えればよいです。

## 属性：他のテーブル

長くなるので割愛しますが、以下のようにしました。

```mermaid
erDiagram
  posts {
    int id PK
    int user_id FK
    string body "投稿内容"
  }
  comments {
    int id PK
    int user_id FK
    int post_id FK
    string body "コメント内容"
    timestamp created_at
  }
  likes {
    int id PK
    int user_id FK
    int post_id FK
    timestamp created_at
  }
```
`FK`は「フォーリンキー（外部キー）」のことで、別のテーブルのプライマリキーを参照する、ということです。
`posts`の`user_id`なら「投稿を作成した`user`を`users`の`id`を参照し識別する」ということですね。

## 関係：`users` vs `posts`

一人のユーザーを想像しましょう。
そのユーザーは複数の投稿をすることができますか？
→はい。

次に、一つの投稿を想像しましょう。
その投稿は複数のユーザーによって書かれましたか？
→いいえ。

ということで、1:Nの関係です。簡単！
```mermaid
erDiagram
  users {
    int id PK
    string name "ユーザーネーム"
    int age "年齢"
    string email "email"
    string password "パスワード"
  }
  posts {
    int id PK
    int user_id FK
    string body "投稿内容"
  }
  users ||--o{ posts: ""
```
このとき、「0であることもある」かどうかで表記が変わります。

https://qiita.com/nakamura_slj/items/896a8c68738a29a4c3aa#%E3%82%AB%E3%83%BC%E3%83%87%E3%82%A3%E3%83%8A%E3%83%AA%E3%83%86%E3%82%A3%E3%81%A8%E3%81%AF

投稿が0のユーザー・・・ありえます。
作成者が0の投稿・・・ありえません。

ということで、先程の図の関係（ `users` : `posts` = 1 : (0 or N) ）になります。
※`-|--`（１）は`-||--`と書くこともあります。今回は後者の書き方。

※2024/06/25 追記 `-|--`（１）と`-||--`（必ず１）が違う意味をもつ場合もあるようです。

https://qiita.com/kohki_takatama/items/7843359a682df15c78bc

## 関係：`users` vs `comments`

一人のユーザーが複数のコメントをする・・・ありえます。
一つのコメントが複数のユーザーによってつくられる・・・ありえません。

コメントが0のユーザー・・・ありえます。
作成者が0のコメント・・・ありえません。

よって、1 : (0 or N)です。

## 関係：`posts` vs `comments`

一つの投稿に複数のコメントがつく・・・ありえます。
一つのコメントが複数の投稿に紐づく・・・ありえません。

コメントが0の投稿・・・ありえます。
投稿に紐づかないコメント・・・ありえません。

よって、1 : (0 or N)です。

## 関係：`likes` vs `users` / `posts`

`comments`と同じです。
### vs `users`

一人のユーザーが複数のいいねをする・・・ありえます。
一つのいいねが複数のユーザーによってされる・・・ありえません。

いいねが0のユーザー・・・ありえます。
ユーザーが0のいいね・・・ありえません。

よって、1 : (0 or N)です。

### vs `posts`

一つの投稿に複数のいいねがつく・・・ありえます。
一つのいいねが複数の投稿に紐づく・・・ありえません。

いいねが0の投稿・・・ありえます。
投稿に紐づかない いいね・・・ありえません。

よって、1 : (0 or N)です。
## 完成！

```mermaid
erDiagram
  users {
    int id PK
    string name "ユーザーネーム"
    int age "年齢"
    string email "email"
    string password "パスワード"
  }
  posts {
    int id PK
    int user_id FK
    string body "投稿内容"
  }
  comments {
    int id PK
    int user_id FK
    int post_id FK
    string body "コメント内容"
    timestamp created_at
  }
  likes {
    int id PK
    int user_id FK
    int post_id FK
    timestamp created_at
  }
  users ||--o{ posts: ""
  users ||--o{ comments: ""
  users ||--o{ likes: ""
  posts ||--o{ comments: ""
  posts ||--o{ likes: ""
```
完成です！！簡単！！

お気づきでしょうか。今回は全て1:Nでした。
とにかく、1:Nが基本です。

# 1:1 / N:Nの例

次に、1:1 / N:Nの例も見てみましょう。

特に、N:Nは1:Nの次によく出てきます。

## 1:1

### 「社員」と「社員証」

社員（社員番号・名前・部署）と、社員証（カード番号・有効期限）は1:1です。
「社員が複数の社員証を持つこと」「社員証が複数の社員に属すること」どちらもあってはなりません。
```mermaid
erDiagram
  employees {
    int id PK
    string name
    enum department
  }
  employee_cards {
    int id PK
    int employee_id FK
    timestamp expiry_date
  }
  employees ||--|| employee_cards: ""
```
## N:N

### 「著者」と「書籍」

一人の著者が複数の書籍を書くこと・・・ありえます。
一つの本が複数の著者に書かれること・・・ありえます。

N:Nですね。このときは中間テーブルを使用します。

```mermaid
erDiagram
  authors {
    int id PK
    string name
  }
  books {
    int id PK
    string title
  }
  author_books {
    int authors_id FK
    int books_id FK
  }
  authors ||--o{ author_books: ""
  books ||--o{ author_books: ""
```

中間テーブルを使用する理由については

https://qiita.com/ramuneru/items/db43589551dd0c00fef9#3-%E5%A4%9A%E5%AF%BE%E5%A4%9A%E3%81%AE%E5%95%8F%E9%A1%8C%E7%82%B9

を読んでいただくのが良いと思います。

そして、中間テーブルを使用すると・・・そうです。1:Nになります。

やはり1:Nが基本なんだ・・・！

# おわりに

以上、実務経験のない初心者の考えた「さいきょうの極意」でしたが、いかがでしたでしょうか。

つっこみどころあると思います。ぜひコメントいただければ幸いです。
<br>

さて、次は何を書こうかしら・・・