mcpField
D
database-schema-designer

database-schema-designer

github.com/softaworksデータ・データベース2.6k4.3k 回インストール34 週間前に更新
リポジトリ

database-schema-designer について

database-schema-designerは、ドメイン要件とアクセスパターンに基づいてSQLまたはNoSQLのスキーマを設計するスキルです。スキーマの生成やレビューに加え、インデックス設計、正規化、ロールバック可能なマイグレーションを扱います。

主な機能

  1. 01SQLとNoSQLのスキーマ生成
  2. 02エンティティと関係のモデル化
  3. 03正規化と非正規化の適用
  4. 04インデックスと制約の設計
  5. 05ロールバック可能なマイグレーション作成
  6. 06既存スキーマのレビュー

活用シーン

  1. 01ECプラットフォームのテーブル設計
  2. 02アクセスパターンに基づくデータモデル選択
  3. 03インデックスによるクエリ性能の改善
  4. 04ロールバック可能なスキーマ変更の計画

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/softaworks/agent-toolkit --skill database-schema-designer

インストール後、Claude Code で/database-schema-designerを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
assets/templates/
migration-template.sql
references/
schema-design-checklist.md

詳細

Database Schema Designer

ベストプラクティスを組み込んだ、本番環境で使えるデータベーススキーマを設計します。


クイックスタート

データモデルを説明するだけです:

design a schema for an e-commerce platform with users, products, orders

次のような完全な SQL スキーマが得られます:

CREATE TABLE users (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  email VARCHAR(255) UNIQUE NOT NULL,
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE orders (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id BIGINT NOT NULL REFERENCES users(id),
  total DECIMAL(10,2) NOT NULL,
  INDEX idx_orders_user (user_id)
);

リクエストに含めるべき内容:

  • エンティティ(ユーザー、商品、注文)
  • 主要なリレーションシップ(ユーザーは注文を持つ、注文は明細を持つ)
  • スケールの目安(高トラフィック、数百万件のレコード)
  • データベースの希望(SQL/NoSQL)- 指定がない場合は SQL がデフォルトです

トリガー

トリガー例
design schema"design a schema for user authentication"
database design"database design for multi-tenant SaaS"
create tables"create tables for a blog system"
schema for"schema for inventory management"
model data"model data for real-time analytics"
I need a database"I need a database for tracking orders"
design NoSQL"design NoSQL schema for product catalog"

主要用語

用語定義
Normalization冗長性を減らすためにデータを整理すること(1NF → 2NF → 3NF)
3NF第3正規形 - 列間に推移的な従属関係がないこと
OLTPオンライントランザクション処理 - 書き込みが多く、正規化が必要
OLAPオンライン分析処理 - 読み取りが多く、非正規化の恩恵を受ける
Foreign Key (FK)他のテーブルの主キーを参照する列
Indexクエリを高速化するデータ構造(書き込みは遅くなる)
Access Patternアプリがデータを読み書きする方法(クエリ、結合、フィルター)
Denormalization読み取りを高速化するために意図的にデータを重複させること

クイックリファレンス

タスクアプローチ重要な考慮事項
新規スキーマまず 3NF に正規化UI よりドメインモデリング
SQL vs NoSQLアクセスパターンで決定読み取り/書き込み比率が重要
主キーINT または UUID分散システムには UUID
外部キー常に制約を設定ON DELETE の戦略が重要
インデックスFK + WHERE 句の列列の順序が重要
マイグレーション常に可逆にする後方互換性を優先

プロセス概要

Your Data Requirements
    |
    v
+-----------------------------------------------------+
| Phase 1: ANALYSIS                                   |
| * Identify entities and relationships               |
| * Determine access patterns (read vs write heavy)   |
| * Choose SQL or NoSQL based on requirements         |
+-----------------------------------------------------+
    |
    v
+-----------------------------------------------------+
| Phase 2: DESIGN                                     |
| * Normalize to 3NF (SQL) or embed/reference (NoSQL) |
| * Define primary keys and foreign keys              |
| * Choose appropriate data types                     |
| * Add constraints (UNIQUE, CHECK, NOT NULL)         |
+-----------------------------------------------------+
    |
    v
+-----------------------------------------------------+
| Phase 3: OPTIMIZE                                   |
| * Plan indexing strategy                            |
| * Consider denormalization for read-heavy queries   |
| * Add timestamps (created_at, updated_at)           |
+-----------------------------------------------------+
    |
    v
+-----------------------------------------------------+
| Phase 4: MIGRATE                                    |
| * Generate migration scripts (up + down)            |
| * Ensure backward compatibility                     |
| * Plan zero-downtime deployment                     |
+-----------------------------------------------------+
    |
    v
Production-Ready Schema

コマンド

コマンド使用するタイミングアクション
design schema for {domain}ゼロから始めるときスキーマを完全に生成
normalize {table}既存テーブルの修正正規化ルールを適用
add indexes for {table}パフォーマンスの問題インデックス戦略を生成
migration for {change}スキーマの進化可逆なマイグレーションを作成
review schemaコードレビュー既存スキーマを監査

ワークフロー: design schema で開始 → normalize で反復 → add indexes で最適化 → migration で進化


コア原則

原則理由実装
ドメインをモデル化するUI は変わるが、ドメインは変わらないエンティティ名はビジネスの概念を反映する
データ整合性を最優先破損の修復はコストが高いデータベースレベルで制約を設定
アクセスパターンに最適化両方を同時に最適化することはできないOLTP: 正規化、OLAP: 非正規化
スケールを見越して計画後から改修するのは大変インデックス戦略とパーティショニング計画

アンチパターン

避けるべきこと理由代わりに
すべてに VARCHAR(255)ストレージの無駄になり、意図が不明確になるフィールドごとに適切なサイズを指定
お金に FLOAT丸め誤差が生じるDECIMAL(10,2)
FK 制約の欠如孤立したデータが発生する外部キーを必ず定義する
FK にインデックスがないJOIN が遅くなるすべての外部キーにインデックスを作成
日付を文字列で保存比較やソートができないDATE、TIMESTAMP 型を使用
クエリで SELECT *不要なデータを取得してしまう列を明示的に指定
不可逆なマイグレーションロールバックできない必ず DOWN マイグレーションを作成
デフォルト値なしで NOT NULL を追加既存の行が壊れるnullable で追加し、値を埋めてから制約を付与

検証チェックリスト

スキーマを設計した後に確認します:

  • すべてのテーブルに主キーがある
  • すべてのリレーションに外部キー制約がある
  • 各外部キーに ON DELETE 戦略が定義されている
  • すべての外部キーにインデックスがある
  • 頻繁に検索される列にインデックスがある
  • 適切なデータ型を使用している(金額には DECIMAL など)
  • 必須項目に NOT NULL が設定されている
  • 必要な箇所に UNIQUE 制約がある
  • 検証用に CHECK 制約がある
  • created_at と updated_at のタイムスタンプがある
  • マイグレーションスクリプトは可逆である
  • 本番データを使ってステージング環境でテストしている

詳細解説: 正規化 (SQL)
正規形
正規形ルール違反例
第1正規形原子値であり、繰り返しグループがないproduct_ids = '1,2,3'
第2正規形第1正規形 + 部分従属がないorder_items 内の customer_name
第3正規形第2正規形 + 推移従属がないpostal_code から導出される country
第1正規形 (1NF)
-- BAD: Multiple values in column
CREATE TABLE orders (
  id INT PRIMARY KEY,
  product_ids VARCHAR(255)  -- '101,102,103'
);

-- GOOD: Separate table for items
CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT
);

CREATE TABLE order_items (
  id INT PRIMARY KEY,
  order_id INT REFERENCES orders(id),
  product_id INT
);
第2正規形 (2NF)
-- BAD: customer_name depends only on customer_id
CREATE TABLE order_items (
  order_id INT,
  product_id INT,
  customer_name VARCHAR(100),  -- Partial dependency!
  PRIMARY KEY (order_id, product_id)
);

-- GOOD: Customer data in separate table
CREATE TABLE customers (
  id INT PRIMARY KEY,
  name VARCHAR(100)
);
第3正規形 (3NF)
-- BAD: country depends on postal_code
CREATE TABLE customers (
  id INT PRIMARY KEY,
  postal_code VARCHAR(10),
  country VARCHAR(50)  -- Transitive dependency!
);

-- GOOD: Separate postal_codes table
CREATE TABLE postal_codes (
  code VARCHAR(10) PRIMARY KEY,
  country VARCHAR(50)
);
非正規化を行う場合
シナリオ非正規化の戦略
読み取りが多いレポート事前計算された集計値
コストの高い JOINキャッシュされた導出列
分析ダッシュボードマテリアライズドビュー
-- Denormalized for performance
CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT,
  total_amount DECIMAL(10,2),  -- Calculated
  item_count INT               -- Calculated
);
詳細解説: データ型
文字列型
型用途例
CHAR(n)固定長州コード、ISO 日付
VARCHAR(n)可変長名前、メールアドレス
TEXT長文コンテンツ記事、説明文
-- Good sizing
email VARCHAR(255)
phone VARCHAR(20)
country_code CHAR(2)
数値型
型範囲用途
TINYINT-128 から 127年齢、ステータスコード
SMALLINT-32K から 32K数量
INT-21億 から 21億ID、件数
BIGINT非常に大きい大きな ID、タイムスタンプ
DECIMAL(p,s)正確な精度金額
FLOAT/DOUBLE近似値科学データ
-- ALWAYS use DECIMAL for money
price DECIMAL(10, 2)  -- $99,999,999.99

-- NEVER use FLOAT for money
price FLOAT  -- Rounding errors!
日付・時刻型
DATE        -- 2025-10-31
TIME        -- 14:30:00
DATETIME    -- 2025-10-31 14:30:00
TIMESTAMP   -- Auto timezone conversion

-- Always store in UTC
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
真偽値
-- PostgreSQL
is_active BOOLEAN DEFAULT TRUE

-- MySQL
is_active TINYINT(1) DEFAULT 1
詳細解説: インデックス戦略
インデックスを作成するタイミング
常にインデックスを作成理由
外部キーJOIN を高速化する
WHERE 句の列フィルタリングを高速化する
ORDER BY の列ソートを高速化する
一意制約一意性を保証する
-- Foreign key index
CREATE INDEX idx_orders_customer ON orders(customer_id);

-- Query pattern index
CREATE INDEX idx_orders_status_date ON orders(status, created_at);
インデックスの種類
種類最適な用途例
B-Tree範囲検索、等値検索price > 100
Hash完全一致のみemail = '[email protected]'
Full-textテキスト検索MATCH AGAINST
Partial一部の行のみWHERE is_active = true
複合インデックスの列順
CREATE INDEX idx_customer_status ON orders(customer_id, status);

-- Uses index (customer_id first)
SELECT * FROM orders WHERE customer_id = 123;
SELECT * FROM orders WHERE customer_id = 123 AND status = 'pending';

-- Does NOT use index (status alone)
SELECT * FROM orders WHERE status = 'pending';

ルール: 最も選択性の高い列を先頭にするか、単独で最も多く検索される列を先頭にします。

インデックスの落とし穴
落とし穴問題解決策
インデックスの過剰作成書き込みが遅くなる検索で使われるものだけにインデックスを作成する
列順の誤りインデックスが使われないクエリのパターンに合わせる
外部キーのインデックス欠如JOIN が遅くなる外部キーには必ずインデックスを作成する
詳細解説: 制約
主キー
-- Auto-increment (simple)
id INT AUTO_INCREMENT PRIMARY KEY

-- UUID (distributed systems)
id CHAR(36) PRIMARY KEY DEFAULT (UUID())

-- Composite (junction tables)
PRIMARY KEY (student_id, course_id)
外部キー
FOREIGN KEY (customer_id) REFERENCES customers(id)
  ON DELETE CASCADE     -- Delete children with parent
  ON DELETE RESTRICT    -- Prevent deletion if referenced
  ON DELETE SET NULL    -- Set to NULL when parent deleted
  ON UPDATE CASCADE     -- Update children when parent changes
戦略使用する場面
CASCADE従属データ(order_items など)
RESTRICT重要な参照(誤操作を防ぐ)
SET NULL任意のリレーション
その他の制約
-- Unique
email VARCHAR(255) UNIQUE NOT NULL

-- Composite unique
UNIQUE (student_id, course_id)

-- Check
price DECIMAL(10,2) CHECK (price >= 0)
discount INT CHECK (discount BETWEEN 0 AND 100)

-- Not null
name VARCHAR(100) NOT NULL
詳細解説: リレーションのパターン
1対多
CREATE TABLE orders (
  id INT PRIMARY KEY,
  customer_id INT NOT NULL REFERENCES customers(id)
);

CREATE TABLE order_items (
  id INT PRIMARY KEY,
  order_id INT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
  product_id INT NOT NULL,
  quantity INT NOT NULL
);
多対多
-- Junction table
CREATE TABLE enrollments (
  student_id INT REFERENCES students(id) ON DELETE CASCADE,
  course_id INT REFERENCES courses(id) ON DELETE CASCADE,
  enrolled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (student_id, course_id)
);
自己参照
CREATE TABLE employees (
  id INT PRIMARY KEY,
  name VARCHAR(100) NOT NULL,
  manager_id INT REFERENCES employees(id)
);
ポリモーフィック
-- Approach 1: Separate FKs (stronger integrity)
CREATE TABLE comments (
  id INT PRIMARY KEY,
  content TEXT NOT NULL,
  post_id INT REFERENCES posts(id),
  photo_id INT REFERENCES photos(id),
  CHECK (
    (post_id IS NOT NULL AND photo_id IS NULL) OR
    (post_id IS NULL AND photo_id IS NOT NULL)
  )
);

-- Approach 2: Type + ID (flexible, weaker integrity)
CREATE TABLE comments (
  id INT PRIMARY KEY,
  content TEXT NOT NULL,
  commentable_type VARCHAR(50) NOT NULL,
  commentable_id INT NOT NULL
);
詳細解説: NoSQL 設計 (MongoDB)
埋め込みと参照の比較
要素埋め込み参照
アクセスパターン一緒に読み取る別々に読み取る
リレーション1:少数1:多数
ドキュメントサイズ小さい16MB に近づく
更新頻度ほとんどない頻繁
Embedded Document
{
  "_id": "order_123",
  "customer": {
    "id": "cust_456",
    "name": "Jane Smith",
    "email": "[email protected]"
  },
  "items": [
    { "product_id": "prod_789", "quantity": 2, "price": 29.99 }
  ],
  "total": 109.97
}
Referenced Document
{
  "_id": "order_123",
  "customer_id": "cust_456",
  "item_ids": ["item_1", "item_2"],
  "total": 109.97
}
MongoDB Indexes
// Single field
db.users.createIndex({ email: 1 }, { unique: true });

// Composite
db.orders.createIndex({ customer_id: 1, created_at: -1 });

// Text search
db.articles.createIndex({ title: "text", content: "text" });

// Geospatial
db.stores.createIndex({ location: "2dsphere" });
Deep Dive: マイグレーション
マイグレーションのベストプラクティス
プラクティス理由
常に可逆にするロールバックが必要になるため
後方互換性を保つダウンタイムなしのデプロイのため
データより先にスキーマを変更する関心事を分離するため
ステージング環境でテストする問題を早期に発見するため
カラムの追加(ダウンタイムなし)
-- Step 1: Add nullable column
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- Step 2: Deploy code that writes to new column

-- Step 3: Backfill existing rows
UPDATE users SET phone = '' WHERE phone IS NULL;

-- Step 4: Make required (if needed)
ALTER TABLE users MODIFY phone VARCHAR(20) NOT NULL;
カラム名の変更(ダウンタイムなし)
-- Step 1: Add new column
ALTER TABLE users ADD COLUMN email_address VARCHAR(255);

-- Step 2: Copy data
UPDATE users SET email_address = email;

-- Step 3: Deploy code reading from new column
-- Step 4: Deploy code writing to new column

-- Step 5: Drop old column
ALTER TABLE users DROP COLUMN email;
マイグレーションのテンプレート
-- Migration: YYYYMMDDHHMMSS_description.sql

-- UP
BEGIN;
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
CREATE INDEX idx_users_phone ON users(phone);
COMMIT;

-- DOWN
BEGIN;
DROP INDEX idx_users_phone ON users;
ALTER TABLE users DROP COLUMN phone;
COMMIT;
Deep Dive: パフォーマンス最適化
クエリ分析
EXPLAIN SELECT * FROM orders
WHERE customer_id = 123 AND status = 'pending';
確認すべき項目意味
type: ALLフルテーブルスキャン(好ましくない)
type: refインデックスを使用(好ましい)
key: NULLインデックスが使用されていない
rows: high多くの行をスキャンしている
N+1 クエリ問題
# BAD: N+1 queries
orders = db.query("SELECT * FROM orders")
for order in orders:
    customer = db.query(f"SELECT * FROM customers WHERE id = {order.customer_id}")

# GOOD: Single JOIN
results = db.query("""
    SELECT orders.*, customers.name
    FROM orders
    JOIN customers ON orders.customer_id = customers.id
""")
最適化テクニック
テクニック使用するタイミング
インデックスを追加するWHERE/ORDER BY が遅い場合
非正規化する高コストな JOIN がある場合
ページネーション大量の結果セットを扱う場合
キャッシュ同じクエリが繰り返し実行される場合
リードレプリカ読み取り負荷が高い場合
パーティショニング非常に大きなテーブルの場合

拡張ポイント

  1. データベース固有のパターン: MySQL、PostgreSQL、SQLite それぞれのバリエーションを追加する
  2. 高度なパターン: 時系列データ、イベントソーシング、CQRS、マルチテナンシー
  3. ORM 統合: TypeORM、Prisma、SQLAlchemy のパターン
  4. モニタリング: クエリパフォーマンスの追跡、スロークエリのアラート

コメント