---
title: "ライブ・フェス機能 テーブル設計書"
---

最終更新: 2026-05-08
関連 Issue: #528, #776

---

## 1. 概要

Vercel Postgres（Neon）上に構築するライブ機能のテーブル設計書。
#527 の結論（Neon 採用・react-leaflet・JIS 2桁コード）を前提とする。

### 対象テーブル

| テーブル | 種別 | 概要 |
| --- | --- | --- |
| `venues` | 新規 | 会場（住所・座標） |
| `tours` | 新規 | ツアー |
| `lives` | 新規 | 公演（日程・会場） |
| `member_lives` | 新規 | メンバーと公演の紐づけ |
| `prefecture_codes` | 新規 | 都道府県コード変換テーブル |
| `members` | 新規 | members.json 連携先（出身地照合用） |
| `setlists` | 新規 | セットリスト（ツアー・フェス別演奏曲目） |

### データストア方針

- ライブ関連データ（本設計書の対象）: **Neon（Vercel Postgres）** を正とする
- 現行データ（メンバー・リリース等）: **Vercel Blob / JSON** を継続
- メンテナンス: VSCode で CSV を直接編集し、シードスクリプトで Neon に投入（#609）

---

## 2. テーブル定義

### 2-1. `venues`（会場）

会場の基本情報と react-leaflet 表示用の座標を管理する。

```sql
CREATE TABLE venues (
  venue_id    VARCHAR(8)    PRIMARY KEY,
  name        VARCHAR(255)  NOT NULL,
  prefecture  CHAR(2)       NOT NULL,
  latitude    DECIMAL(9,6),
  longitude   DECIMAL(9,6)
);
```

| カラム名 | 型 | NOT NULL | 説明 |
| --- | --- | --- | --- |
| `venue_id` | VARCHAR(8) | ✅ | PK。都道府県 JIS 2桁 + 連番 3桁（例: `13001`） |
| `name` | VARCHAR(255) | ✅ | 会場名（例: `日本武道館`） |
| `prefecture` | CHAR(2) | ✅ | JIS 2桁コード（例: `13`）。先頭ゼロ保持のため文字列型 |
| `latitude` | DECIMAL(9,6) | — | 緯度（WGS84。例: `35.693200`）。未登録時は NULL |
| `longitude` | DECIMAL(9,6) | — | 経度（WGS84。例: `139.749700`）。未登録時は NULL |

**備考:**
- `DECIMAL(9,6)` は整数部 3桁・小数部 6桁（精度 約 0.1m）
- PostGIS 不使用（精細な地図表示は不要という #527 の方針に準拠）
- 座標未登録の会場はマップ上に表示しない

**インデックス:**
```sql
CREATE INDEX idx_venues_prefecture ON venues (prefecture);
```

---

### 2-2. `tours`（ツアー）

ツアー単位の情報を管理する。

```sql
CREATE TABLE tours (
  tour_id     VARCHAR(20)   PRIMARY KEY,
  title       VARCHAR(255)  NOT NULL,
  release_id  VARCHAR(255)
);
```

| カラム名 | 型 | NOT NULL | 説明 |
| --- | --- | --- | --- |
| `tour_id` | VARCHAR(20) | ✅ | PK。年 + 識別子（例: `2026B`） |
| `title` | VARCHAR(255) | ✅ | ツアー名称（例: `モーニング娘。'26 コンサートツアー春 – Rays Of Light –`） |
| `release_id` | VARCHAR(255) | — | 関連リリース ID（releases.json の `id` と対応。FK 制約なし） |

**備考:**
- `release_id` は Blob 管理の releases.json を参照するため、DB レベルの FK 制約は設けない

---

### 2-3. `lives`（公演）

個別公演の日程・会場・所属ツアーを管理する。

```sql
CREATE TABLE lives (
  live_id   VARCHAR(20)   PRIMARY KEY,
  tour_id   VARCHAR(20)   REFERENCES tours (tour_id),
  venue_id  VARCHAR(8)    NOT NULL REFERENCES venues (venue_id),
  date      DATE          NOT NULL,
  time      TIME,
  title     VARCHAR(255)
);
```

| カラム名 | 型 | NOT NULL | 説明 |
| --- | --- | --- | --- |
| `live_id` | VARCHAR(20) | ✅ | PK。ツアー ID + 連番 3桁（例: `2026B001`） |
| `tour_id` | VARCHAR(20) | — | FK → tours(tour_id)。単独公演は NULL |
| `venue_id` | VARCHAR(8) | ✅ | FK → venues(venue_id) |
| `date` | DATE | ✅ | 公演日（例: `2026-04-11`） |
| `time` | TIME | — | 開演時刻（例: `13:00:00`）。`00:00` は時刻不明を意味し、画面上は時刻を表示しない |
| `title` | VARCHAR(255) | — | 公演タイトル。ツアー名と異なる場合のオーバーライド用 |

**インデックス:**
```sql
CREATE INDEX idx_lives_tour_id  ON lives (tour_id);
CREATE INDEX idx_lives_venue_id ON lives (venue_id);
CREATE INDEX idx_lives_date     ON lives (date);
```

---

### 2-4. `member_lives`（メンバー × 公演紐づけ）

メンバーと公演の多対多関係を管理する（#517 で使用）。

```sql
CREATE TABLE member_lives (
  member_id  VARCHAR(50)   NOT NULL,
  live_id    VARCHAR(20)   NOT NULL REFERENCES lives (live_id),
  PRIMARY KEY (member_id, live_id)
);
```

| カラム名 | 型 | NOT NULL | 説明 |
| --- | --- | --- | --- |
| `member_id` | VARCHAR(50) | ✅ | members.json の `id` と対応。FK 制約なし |
| `live_id` | VARCHAR(20) | ✅ | FK → lives(live_id) |

**備考:**
- `member_id` は Blob 管理の members.json を参照するため、DB レベルの FK 制約は設けない
- PK = (member_id, live_id) の複合主キーで重複を防ぐ

**インデックス:**
```sql
CREATE INDEX idx_member_lives_live_id ON member_lives (live_id);
```

---

### 2-5. `prefecture_codes`（都道府県コード変換）

`members.birthplace`（自由文字列）を JIS 2桁コードに変換するための参照テーブル（#520 ご当地ライブ検出で使用）。

```sql
CREATE TABLE prefecture_codes (
  prefecture_code  CHAR(2)       PRIMARY KEY,
  name             VARCHAR(20)   NOT NULL,
  aliases          TEXT[]
);
```

| カラム名 | 型 | NOT NULL | 説明 |
| --- | --- | --- | --- |
| `prefecture_code` | CHAR(2) | ✅ | PK。JIS 2桁コード（例: `13`） |
| `name` | VARCHAR(20) | ✅ | 都道府県名（例: `東京都`） |
| `aliases` | TEXT[] | — | 別表記の配列（例: `{"東京", "東京 ", "Tokyo"}`） |

**備考:**
- `aliases` は `birthplace` の表記ゆれ吸収のために使用（部分一致マッチング）
- 初期データ: 47都道府県分を CSV（`prefecture.csv`）からシード投入
- **海外拡張コード（#674）**: 海外公演対応のため以下3コードを追加（`aliases` は空）

| `prefecture_code` | `name` | 対象地域 |
| --- | --- | --- |
| `50` | アジア | 韓国・台湾・香港・中国など |
| `60` | ヨーロッパ | フランスなど |
| `70` | 北中米 | 米国・メキシコなど |

---

### 2-6. `members`（出身地照合用）

Blob の members.json から出身地情報のみを複製し、会場の都道府県との照合に使用する（#520 専用テーブル）。

```sql
CREATE TABLE members (
  member_id   VARCHAR(50)   PRIMARY KEY,
  birthplace  VARCHAR(255)
);
```

| カラム名 | 型 | NOT NULL | 説明 |
| --- | --- | --- | --- |
| `member_id` | VARCHAR(50) | ✅ | PK。members.json の `id` と一致 |
| `birthplace` | VARCHAR(255) | — | 出身地の自由文字列（例: `東京都港区`）。未設定時は NULL |

**備考:**
- members.json の `profile.birthplace` を定期同期または手動投入
- 都道府県コードへの変換は `prefecture_codes` テーブルとのマッチングでアプリ層が担当
- この `members` テーブルは Blob の members.json を置き換えるものではない

---

### 2-7. `festivals`（フェス）

参加フェスの開催情報を管理する。複数日開催フェスのうち参加する1日を1レコードとして管理する。

```sql
CREATE TABLE festivals (
  festival_id  VARCHAR(50)   PRIMARY KEY,
  name         VARCHAR(255)  NOT NULL,
  date         DATE          NOT NULL,
  venue_id     VARCHAR(8)    NOT NULL REFERENCES venues (venue_id)
);
```

| カラム名 | 型 | NOT NULL | 説明 |
| --- | --- | --- | --- |
| `festival_id` | VARCHAR(50) | ✅ | PK。例: `2025-ROCKINJAPAN-1` |
| `name` | VARCHAR(255) | ✅ | フェス名（例: `ROCK IN JAPAN FESTIVAL 2025`） |
| `date` | DATE | ✅ | 参加日 |
| `venue_id` | VARCHAR(8) | ✅ | FK → venues(venue_id) |

**インデックス:**
```sql
CREATE INDEX idx_festivals_venue_id ON festivals (venue_id);
CREATE INDEX idx_festivals_date     ON festivals (date);
```

---

### 2-8. `setlists`（セットリスト）

ツアーまたはフェスの演奏曲目・順序を管理する（#775）。

```sql
CREATE TABLE setlists (
  setlist_id   VARCHAR(80)   PRIMARY KEY,
  type         VARCHAR(10)   NOT NULL,
  tour_id      VARCHAR(20)   REFERENCES tours (tour_id) ON DELETE SET NULL,
  festival_id  VARCHAR(50)   REFERENCES festivals (festival_id) ON DELETE SET NULL,
  seq          INT           NOT NULL,
  track_id     VARCHAR(255)  NOT NULL
);
```

| カラム名 | 型 | NOT NULL | 説明 |
| --- | --- | --- | --- |
| `setlist_id` | VARCHAR(80) | ✅ | PK。`{tourId or festivalId}-{3桁連番}`（例: `T2025-01-001`） |
| `type` | VARCHAR(10) | ✅ | 種別: `tour` or `festival` |
| `tour_id` | VARCHAR(20) | — | FK → tours(tour_id)。ツアーセットリストの場合に設定 |
| `festival_id` | VARCHAR(50) | — | FK → festivals(festival_id)。フェスセットリストの場合に設定 |
| `seq` | INT | ✅ | 演奏順（1始まり） |
| `track_id` | VARCHAR(255) | ✅ | 曲の識別子（releases.json の track ID と対応。FK 制約なし） |

**備考:**
- `tour_id` と `festival_id` は排他的（いずれか一方のみ設定）
- `track_id` は Blob 管理の releases.json を参照するため、DB レベルの FK 制約は設けない
- 画面表示時に `track_id` を releases.json で解決し、曲名・YouTube 動画 ID を取得する

**インデックス:**
```sql
CREATE INDEX idx_setlists_tour_id     ON setlists (tour_id);
CREATE INDEX idx_setlists_festival_id ON setlists (festival_id);
```

---

## 3. ER 図（ライブ機能テーブル）

```mermaid
erDiagram
  tours {
    varchar tour_id PK
    varchar title
    varchar release_id "Blob releases.json 参照（FK制約なし）"
  }

  venues {
    varchar venue_id PK
    varchar name
    char    prefecture "JIS 2桁コード"
    decimal latitude  "緯度 DECIMAL(9,6)"
    decimal longitude "経度 DECIMAL(9,6)"
  }

  lives {
    varchar live_id   PK
    varchar tour_id   FK
    varchar venue_id  FK
    date    date
    time    time      "開演時刻（任意）"
    varchar title     "オーバーライド用（任意）"
  }

  member_lives {
    varchar member_id "Blob members.json 参照（FK制約なし）"
    varchar live_id   FK
  }

  prefecture_codes {
    char    prefecture_code PK
    varchar name
    text[]  aliases         "別表記（任意）"
  }

  members {
    varchar member_id  PK "Blob members.json の id と一致"
    varchar birthplace "自由文字列（任意）"
  }

  setlists {
    varchar setlist_id  PK
    varchar type        "tour or festival"
    varchar tour_id     FK "任意"
    varchar festival_id FK "任意"
    int     seq
    varchar track_id    "Blob releases.json 参照（FK制約なし）"
  }

  festivals {
    varchar festival_id PK
    varchar name
    date    date
    varchar venue_id    FK
  }

  tours    ||--o{ lives         : "公演"
  venues   ||--o{ lives         : "開催会場"
  lives    ||--o{ member_lives  : "出演メンバー"
  tours    ||--o{ setlists      : "セットリスト"
  festivals ||--o{ setlists     : "セットリスト"
  venues   ||--o{ festivals     : "開催会場"
```

---

## 4. CSV 投入フォーマット（シード用）

#609（Neon スキーマ実装）でのシード投入に使用する CSV フォーマット。

### `venues.csv`

```csv
venueId,name,prefecture,latitude,longitude
13001,日本武道館,13,35.693200,139.749700
```

### `tours.csv`

```csv
tourId,title,releaseId
2026B,モーニング娘。'26 コンサートツアー春 – Rays Of Light –,
```

### `lives.csv`

```csv
tourId,liveId,date,time,venueId
2026B,2026B001,2026-04-11,13:00,13002
```

### `prefecture.csv`

```csv
prefecture,name
01,北海道
13,東京都
```

### `festivals.csv`

```csv
festivalId,name,date,venueId
2025-SUMMERSONIC-TK,SUMMER SONIC 2025（東京）,2025/8/16,11005
```

### `setlists.csv`

```csv
setlistId,type,tourId,festivalId,seq,trackId
```

---

## 5. 設計上の注意点

### `prefecture` フィールドの型

`CHAR(2)` を使用し、先頭ゼロを文字列として保持する。`INTEGER` や `SMALLINT` にすると `01` → `1` に変換されて照合が崩れる。

### Blob データとの FK 制約

`member_id`（members.json 参照）・`release_id`（releases.json 参照）は Blob 管理のデータを参照するため、DB レベルの外部キー制約を設けない。整合性はアプリ層で担保する。

### 座標精度

`DECIMAL(9,6)` は整数部 3桁 + 小数部 6桁で、精度 約 0.11m（11cm）。react-leaflet での表示用途に十分。PostGIS 導入は不要。

---

## 6. 更新履歴

| 版 | 更新日 | 変更内容 | 関連 Issue |
| --- | --- | --- | --- |
| 1.3 | 2026-05-09 | `setlists` テーブル定義を実装仕様に更新（`tour_id`・`festival_id` FK、`track_id` 参照） | #775 |
| 1.2 | 2026-05-08 | `festivals` テーブル定義を追加 | #776 |
| 1.1 | 2026-04-11 | `lives.time` の `00:00` = 時刻不明仕様を明記 | #528 |
| 1.0 | 2026-04-11 | 初版作成 | #528 |
