0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【DB設計】トレーニング管理アプリにおける中間テーブルの重要性

はじめに

データベース設計において、多対多の関係を適切に管理することは非常に重要です。特に複数のエンティティ間の関連データを扱うアプリケーションでは、中間テーブルが必須となります。

今回は私が開発している野球トレーニング管理アプリを例に、中間テーブルtraining_notesの必要性とその役割について解説します。

アプリケーションの概要

このアプリケーションは、野球選手とコーチがトレーニングを記録・管理するためのものです。主な機能は以下の通りです:

  • コーチがトレーニングメニューを作成
  • 選手が日々のトレーニング実績を記録
  • コーチが選手の実績を確認・コメント

テーブル設計

まず、アプリケーションの主要テーブル構成を簡単に紹介します。

主要テーブル

  • users: ユーザー管理(選手/コーチ)
  • profiles: ユーザーの詳細情報
  • notes: 日々の記録(野球ノート)
  • trainings: トレーニングメニューマスタ
  • comments: ノートへのコメント
  • training_notes: トレーニング実績を記録する中間テーブル ← 今回の主役
CREATE TABLE users (
  id UUID PRIMARY KEY,
  firebase_uid str UNIQUE NOT NULL,
  email VARCHAR(255) NOT NULL UNIQUE,
  role TINYINT NOT NULL DEFAULT 'user', -- 0:player, 1:coach
  -- 他のカラム省略
);

CREATE TABLE notes (
  id UUID PRIMARY KEY,
  user_id UUID NOT NULL,
  theme VARCHAR(255) NOT NULL,
  -- 他のカラム省略
  FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE trainings (
  id UUID PRIMARY KEY,
  user_id UUID NOT NULL,
  menu TEXT NOT NULL,
  -- 他のカラム省略
  FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE TABLE training_notes (
  id UUID PRIMARY KEY,
  training_id UUID NOT NULL,
  note_id UUID NOT NULL,
  count int NOT NULL,
  -- 他のカラム省略
  FOREIGN KEY (training_id) REFERENCES trainings(id),
  FOREIGN KEY (note_id) REFERENCES notes(id)
);

なぜ中間テーブルが必要なのか?

当初、私はトレーニングの実績データを直接trainingsテーブルに保存しようと考えていました。例えば:

CREATE TABLE trainings (
  id UUID PRIMARY KEY,
  user_id UUID NOT NULL,
  menu TEXT NOT NULL,
  count int NULL, -- ここに回数を記録
  -- 他のカラム
);

しかし、この設計には以下の問題がありました:

問題点1: データ入力のタイミングと責任者が異なる

  • menu: コーチが作成時に入力
  • count: 選手が実施後に入力

これにより、レコード作成時にcountが空になってしまいます。

問題点2: 同じメニューを複数選手が実施する場合の重複

例えば「腹筋」というメニューを複数の選手が行う場合、同じメニュー情報が選手の数だけ重複して保存されてしまいます。

問題点3: 履歴管理ができない

同じ選手が同じメニューを日付違いで行った場合の記録ができません。

中間テーブルによる解決

これらの問題を解決するために、training_notesという中間テーブルを導入しました。

メリット1: 責任の分離

  • コーチはtrainingsテーブルにメニューのみを登録
  • 選手はnotesテーブルに日々の記録を作成
  • 実績データ(回数)はtraining_notesで関連付け

メリット2: データの正規化

  • メニュー情報はtrainingsテーブルに一度だけ保存
  • 実績データはtraining_notesテーブルに保存

メリット3: 柔軟なデータ管理

  • 日付別・選手別の実績を個別に記録可能
  • 同じメニューの履歴を時系列で管理可能

実際のデータフロー

サンプルデータ

# usersテーブル
id | name
---+-----
1  | A
2  | B
3  | C

# notesテーブル
id | user_id | title
---+---------+-------
1  | 1       | アアア
2  | 2       | いいい
3  | 3       | ううう
4  | 1       | えええ

# trainingsテーブル
id | menu
---+------
1  | 腹筋
2  | 背筋
3  | 素振り

# training_notesテーブル
id | note_id | training_id | count
---+---------+-------------+------
1  | 1       | 1           | 10
2  | 1       | 2           | 10
3  | 2       | 1           | 20

クエリ例: 選手Aのトレーニング実績を取得

SELECT 
  u.name AS 選手名,
  n.title AS ノートタイトル,
  t.menu AS トレーニングメニュー,
  tn.count AS 回数
FROM users u
JOIN notes n ON u.id = n.user_id
JOIN training_notes tn ON n.id = tn.note_id
JOIN trainings t ON tn.training_id = t.id
WHERE u.id = 1;

結果:

選手名 | ノートタイトル | トレーニングメニュー | 回数
------+---------------+------------------+-----
A     | アアア        | 腹筋              | 10
A     | アアア        | 背筋              | 10
A     | えええ        | 腹筋              | 40
A     | えええ        | 背筋              | 50

まとめ

中間テーブルtraining_notesは一見すると複雑さを増すように思えますが、以下の利点をもたらします:

  1. データ整合性の確保: メニューと実績を適切に分離
  2. 責任の明確化: コーチはメニュー作成、選手は実績入力
  3. 柔軟なデータ管理: 同じメニューの異なる実施回数を記録可能
  4. 効率的なデータ分析: 選手別・日付別・メニュー別の集計が容易

適切な中間テーブルの設計は、アプリケーションの拡張性と保守性を大きく向上させます。多対多の関係を持つデータを扱う際は、ぜひ中間テーブルの導入を検討してみてください。


今回は野球トレーニング管理アプリを例に説明しましたが、この考え方は商品と注文、ユーザーとグループなど、様々な多対多関係に応用できます。皆さんのDB設計の参考になれば幸いです!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?