オプション
家 Skill データベース管理 sql-database-assistant

sql-database-assistant

alirezarezvani/claude-skills alirezarezvani/claude-skills

自然言語をSQLクエリに変換し、データベースのパフォーマンスを最適化し、マイグレーションを生成し、スキーマを調査し、PostgreSQL、MySQL、SQLite、SQL Serverの各データベースでORMを活用します。

...すべて拡張します
2
更新された時間 2026年9月2日

SQL Database Assistant - POWERFUL Tier スキル

概要

データベース設計を支える運用スキルです。「データベース・デザイナー」がスキーマアーキテクチャに焦点を当て、「データベース・スキーマ・デザイナー」がERDモデリングを担当する一方で、このスキルはクエリの作成、パフォーマンスの最適化、マイグレーションの生成、そしてアプリケーションコードとデータベースエンジン間の橋渡しといった日常業務を網羅します。

中核となる機能

  • 自然言語からSQLへ— 要件を正確で高性能なクエリに変換
  • スキーマ探索— PostgreSQL、MySQL、SQLite、SQL Server などの稼働中のデータベースを詳細に分析
  • クエリの最適化— EXPLAIN 分析、インデックスの推奨、N+1 問題の検出、リライトパターン
  • マイグレーション生成— アップグレード/ダウングレードスクリプト、ダウンタイムゼロ戦略、ロールバック計画
  • ORM 統合— Prisma、Drizzle、TypeORM、SQLAlchemy のパターンとエスケープハッチ
  • マルチデータベース対応— 互換性ガイダンス付きの方言対応SQL

ツール

スクリプト 目的
scripts/query_optimizer.py パフォーマンス上の問題に関するSQLクエリの静的解析
scripts/migration_generator.py 変更内容の記述からマイグレーションファイルのテンプレートを生成する
scripts/schema_explorer.py イントロスペクションクエリからスキーマドキュメントを生成する

自然言語からSQLへの変換

変換パターン

要件をSQLに変換する際は、以下の手順に従ってください:

  1. エンティティを特定する— 名詞をテーブルにマッピングする
  2. 関係を特定する— 動詞をJOINまたはサブクエリにマッピングする
  3. フィルタを特定する— 形容詞や条件をWHERE句にマッピングする
  4. 集計を特定する— 「合計」、「平均」、「件数」を GROUP BY にマッピングする
  5. 並べ替えを特定する— 「上位」、「最新」、「最高」を ORDER BY + LIMIT にマッピングする

一般的なクエリテンプレート

グループごとの上位N件(ウィンドウ関数)

SELECT * FROM (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rn
  FROM employees
) ranked WHERE rn <= 3;

累積合計

SELECT date, amount,
  SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM transactions;

ギャップ検出

SELECT curr.id, curr.seq_num, prev.seq_num AS prev_seq
FROM records curr
LEFT JOIN records prev ON prev.seq_num = curr.seq_num - 1
WHERE prev.id IS NULL AND curr.seq_num > 1;

UPSERT (PostgreSQL)

INSERT INTO settings (key, value, updated_at)
VALUES ('theme', 'dark', NOW())
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = EXCLUDED.updated_at;

UPSERT (MySQL)

INSERT INTO settings (key_name, value, updated_at)
VALUES ('theme', 'dark', NOW())
ON DUPLICATE KEY UPDATE value = VALUES(value), updated_at = VALUES(updated_at);

JOIN、CTE、ウィンドウ関数、JSON操作などについては、references/query_patterns.md を参照してください。

スキーマの探索

イントロスペクションクエリ

PostgreSQL — テーブルとカラムのリスト表示

SELECT table_name, column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public'
ORDER BY table_name, ordinal_position;

PostgreSQL — 外部キー

SELECT tc.table_name, kcu.column_name,
  ccu.table_name AS foreign_table, ccu.column_name AS foreign_column
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY';

MySQL — テーブルのサイズ

SELECT table_name, table_rows,
  ROUND(data_length / 1024 / 1024, 2) AS data_mb,
  ROUND(index_length / 1024 / 1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY data_length DESC;

SQLite — スキーマのダンプ

SELECT name, sql FROM sqlite_master WHERE type = 'table' ORDER BY name;

SQL Server — データ型付きの列

SELECT t.name AS table_name, c.name AS column_name,
  ty.name AS data_type, c.max_length, c.is_nullable
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
ORDER BY t.name, c.column_id;

スキーマからのドキュメント生成

scripts/schema_explorer.pyを使用して、Markdown または JSON 形式のドキュメントを生成します:

python scripts/schema_explorer.py --dialect postgres --tables all --format md
python scripts/schema_explorer.py --dialect mysql --tables users,orders --format json --json

クエリの最適化

EXPLAIN 分析のワークフロー

  1. EXPLAIN ANALYZE(PostgreSQL) またはEXPLAIN FORMAT=JSON(MySQL)を実行します
  2. コストが最も高いノードを特定する— 大規模テーブルでのシーケンシャルスキャン、行数の推定値が高いネストループ
  3. インデックスの欠落を確認する— フィルタリングされた列でのシーケンシャルスキャン
  4. 推定値の誤りを確認する— 計画行数と実際行数の乖離は、統計情報の古さを示唆している
  5. JOIN順序を評価する— 結果セットが最も小さいものがJOINを主導するようにする

インデックス推奨チェックリスト

  • WHERE句に含まれる、選択性の高い列
  • JOIN条件に含まれる列(外部キー)
  • LIMIT と組み合わされた ORDER BY 句内の列
  • 複数列のWHERE述語に一致する複合インデックス(選択性の最も高い列を先頭に)
  • 定数フィルターを含むクエリ用の部分インデックス(例:WHERE status = 'active'
  • 読み取り中心のクエリにおけるテーブル参照を回避するためのカバリングインデックス

クエリ書き換えパターン

アンチパターン 書き換え
SELECT * FROM orders SELECT id, status, total FROM orders(明示的な列)
WHERE YEAR(created_at) = 2025 WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01'(SARG可能)
SELECT 内の相関サブクエリ 集計を伴う LEFT JOIN
NULL を含むNOT IN (SELECT ...) NOT EXISTS (SELECT 1 ...)
不要な場合のUNION(重複排除) UNION ALL
LIKE '%search%' 全文検索インデックス (GIN/FULLTEXT)
ORDER BY RAND() アプリケーション側でのランダムサンプリングまたはTABLESAMPLE

N+1 検出

症状:

  • 親行ごとに1つのクエリを実行するアプリケーションのループ
  • ループ内でORMによる関連エンティティの遅延読み込みが行われている
  • クエリログに、IDが異なるものの同一のSELECTパターンが数百件表示される

解決策:

  • イアガーロードを使用する(Prismaではinclude、SQLAlchemyではjoinedload
  • WHERE id IN (...)を使用してクエリをバッチ処理する
  • GraphQLリゾルバーにはDataLoaderパターンを使用する

静的解析ツール

python scripts/query_optimizer.py --query "SELECT * FROM orders WHERE status = 'pending'" --dialect postgres
python scripts/query_optimizer.py --query queries.sql --dialect mysql --json

EXPLAIN プランの読み方、インデックスの種類、および接続プーリングについては、references/optimization_guide.md を参照してください。

マイグレーションの生成

ダウンタイムゼロのマイグレーションパターン

列の追加(安全)

-- 追加
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- 削除
ALTER TABLE users DROP COLUMN phone;

列名の変更(展開・縮小)

-- ステップ1: 新しい列を追加
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
-- ステップ 2: バックフィル
UPDATE users SET full_name = name;
-- ステップ 3: 両方の列を読み取るアプリケーションをデプロイ
-- ステップ 4: 新しい列のみを書き込むアプリケーションをデプロイ
-- ステップ 5: 古い列を削除
ALTER TABLE users DROP COLUMN name;

NOT NULL 列の追加(安全なシーケンス)

-- 手順 1: NULL 許容の列を追加
ALTER TABLE orders ADD COLUMN region VARCHAR(50);
-- 手順 2: デフォルト値でバックフィル
UPDATE orders SET region = 'unknown' WHERE region IS NULL;
-- ステップ 3: 制約を追加
ALTER TABLE orders ALTER COLUMN region SET NOT NULL;
ALTER TABLE orders ALTER COLUMN region SET DEFAULT 'unknown';

インデックスの作成(ノンブロッキング、PostgreSQL)

CREATE INDEX CONCURRENTLY idx_orders_status ON orders (status);

データのバックフィル戦略

  • バッチ更新— ロック競合を避けるため、1000~10000行単位で処理する
  • バックグラウンドジョブ— 進行状況を追跡しながらバックフィルを非同期で実行
  • デュアル書き込み— 移行期間中は、旧列と新列の両方に書き込む
  • 検証クエリ— 各バッチ処理後に、行数とデータの整合性を確認する

ロールバック戦略

すべての移行には、元に戻せるダウンスクリプトが必要です。元に戻せない変更については:

  1. 実行前のバックアップ— 影響を受けるテーブルをpg_dump でバックアップ
  2. 機能フラグ— アプリケーションで旧スキーマと新スキーマの読み取りを切り替え可能
  3. シャドウテーブル— 移行期間中は元のテーブルのコピーを保持する

マイグレーション生成ツール

python scripts/migration_generator.py --change "add email_verified boolean to users" --dialect postgres --format sql
python scripts/migration_generator.py --change "customers テーブルの列名を full_name に変更" --dialect mysql --format alembic --json

マルチデータベース対応

方言の違い

機能 PostgreSQL MySQL SQLite SQL Server
UPSERT 競合時に更新 重複キーがある場合は更新 競合時に更新 MERGE
ブール値 ネイティブBOOLEAN TINYINT(1) INTEGER BIT
自動インクリメント SERIAL/GENERATED AUTO_INCREMENT INTEGER PRIMARY KEY IDENTITY
JSON JSONB(インデックス付き) JSONINTEGER PRIMARY KEY テキスト (ext) NVARCHAR(MAX)
配列 ネイティブARRAY サポートされていません サポートされていません サポートされていません
CTE(再帰的) 完全対応 8.0以上 3.8.3 以降 完全対応
ウィンドウ関数 完全対応 8.0 以上 3.25.0 以降 完全対応
全文検索 tsvector+ GIN FULLTEXTインデックス FTS5 拡張機能 全文検索カタログ
LIMIT/OFFSET LIMIT n OFFSET m LIMIT n OFFSET m LIMIT n OFFSET m OFFSET m ROWS FETCH NEXT n ROWS ONLY

互換性に関するヒント

  • 常にパラメータ化されたクエリを使用してください— すべての方言において SQL インジェクションを防止します
  • 共有コードでは方言固有の関数の使用を避けてください— アダプタ層でラップしてください
  • 移行は対象のエンジン上でテストしてください`information_schema` はエンジンごとに異なります
  • ISO日付形式を使用してください 「YYYY-MM-DD」はどのエンジンでも機能します
  • 識別子は引用符で囲む— ダブルクォート(SQL標準)またはバッククォート(MySQL)を使用する

ORMパターン

Prisma

スキーマ定義

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
}

model Post {
  id       Int    @id @default(autoincrement())
  title    String
  author   User   @relation(fields: [authorId], references: [id])
  authorId Int
}

マイグレーション:npx prisma migrate dev --name add_user_email クエリ API:prisma.user.findMany({ where: { email: { contains: '@' } }, include: { posts: true } }) 生の SQL エスケープハッチ:prisma.$queryRaw\SELECT * FROM users WHERE id = ${userId}``

Drizzle

スキーマファーストの定義

export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  name: text('name'),
  createdAt: timestamp('created_at').defaultNow(),
});

クエリビルダー:db.select().from(users).where(eq(users.email, email)) マイグレーション:npx drizzle-kit generate:pgの後、npx drizzle-kit push:pg

TypeORM

エンティティデコレータ

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ unique: true })
  email: string;

  @OneToMany(() => Post, post => post.author)
  posts: Post[];
}

リポジトリパターン:userRepo.find({ where: { email }, relations: ['posts'] }) マイグレーション:npx typeorm migration:generate -n AddUserEmail

SQLAlchemy

宣言型モデル

class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    email = Column(String(255), unique=True, nullable=False)
    name = Column(String(255))
    posts = relationship('Post', back_populates='author')

セッション管理: 常にwith Session() として session:コンテキストマネージャーを使用する Alembic マイグレーション:alembic revision --autogenerate -m "add user email"

各ORMごとの並列比較およびマイグレーションワークフローについては、references/orm_patterns.mdを参照してください。

データの整合性

制約戦略

  • 主キー— すべてのテーブルに1つ必須。代理キー(シリアル/UUID)を推奨
  • 外部キー— 参照整合性を強制し、ON DELETE の挙動を明示的に定義する
  • UNIQUE 制約— ビジネスレベルの一意性を確保するため(メールアドレス、スラッグ、API キー)
  • CHECK制約— 範囲、列挙型、およびビジネスルールをDBレベルで検証する
  • NOT NULL— デフォルトはNOT NULLとする;真にオプションの場合のみNULL許可とする

トランザクションの隔離レベル

レベル ダーティリード 非再帰読み取り ファントム読み取り ユースケース
未コミットの読み取り はい はい はい 推奨されません
コミット済みとして読み取り いいえ はい はい PostgreSQLのデフォルト、一般的なOLTP
REPEATABLE READ いいえ いいえ はい(InnoDB:いいえ) 金融計算
SERIALIZABLE いいえ いいえ いいえ クリティカルな一貫性(請求、在庫)

デッドロック防止

  1. 一貫性のあるロック順序— 常に同じテーブル/行の順序でロックを取得する
  2. 短いトランザクション— 最初のロックからコミットまでの時間を最小限に抑える
  3. アドバイザリロック— アプリケーションレベルの調整にはpg_advisory_lock()を使用する
  4. 再試行ロジック— デッドロックエラーを検出し、指数関数的バックオフを用いて再試行する

バックアップと復元

PostgreSQL

# フルバックアップ
pg_dump -Fc --no-owner dbname > backup.dump
# 復元
pg_restore -d dbname --clean --no-owner backup.dump
# 特定時点への復旧:WALアーカイブとrestore_commandの設定

MySQL

# フルバックアップ
mysqldump --single-transaction --routines --triggers dbname > backup.sql
# 復元
mysql dbname < backup.sql
# 特定時点復元(PITR)用のバイナリログ:mysqlbinlog --start-datetime="2025-01-01 00:00:00" binlog.000001

SQLite

# バックアップ(同時読み取りに対応)
sqlite3 dbname ".backup backup.db"

バックアップのベストプラクティス

  • 自動化— cron または systemd タイマーを使用し、手動のみには決してしない
  • 復元テスト— テストされていないバックアップはバックアップとは言えない
  • オフサイトコピー— S3、GCS、または別のリージョン
  • 保存期間ポリシー— 毎日 7 日間、毎週 4 週間、毎月 12 ヶ月
  • バックアップのサイズと所要時間を監視する— 急激な変化は問題の兆候

アンチパターン

アンチパターン 問題 修正
SELECT * 不要なデータが転送され、スキーマの変更で動作しなくなる 明示的な列リスト
外部キー列にインデックスが設定されていない JOIN処理の遅延およびカスケード削除 すべての外部キーにインデックスを追加する
N+1クエリ データベースへの1+N回の往復通信 イージーローディングまたはバッチクエリ
暗黙的な型変換 WHERE id = '123'ではインデックスが使用されない 述語内の型の整合性
接続プールの未使用 負荷がかかると接続が枯渇する PgBouncer、ProxySQL、またはORMプール
制限のないクエリ LIMITがないため、数百万行が返されるリスクがある 常にページネーションを行う
金額をFLOAT型で保存 丸め誤差 DECIMAL(19,4)または整数のセントを使用する
「Godテーブル」 50以上の列を持つ1つのテーブル 正規化するか、垂直パーティショニングを使用する
至る所でソフト削除 WHERE deleted_at IS NULLによってすべてのクエリが複雑化する テーブルのアーカイブまたはイベントソーシング
生の文字列連結 SQLインジェクション 常にパラメータ化されたクエリを使用する

相互参照

スキル リレーションシップ
database-designer スキーマアーキテクチャ、正規化分析、ERD生成
database-schema-designer ERDのビジュアルモデリング、リレーションシップのマッピング
移行アーキテクト 複雑な多段階移行のオーケストレーション
API設計レビューツール APIエンドポイントがクエリパターンと整合していることを確認
可観測性プラットフォーム クエリパフォーマンスの監視、低速クエリのアラート
GitHubで見る
---
name: sql-database-assistant
description: Translate natural language into SQL queries, optimize database performance, generate migrations, explore schemas, and work with ORMs across PostgreSQL, MySQL, SQLite, and SQL Server.
---

# SQL Database Assistant - POWERFUL Tier Skill

## Overview

The operational companion to database design. While **database-designer** focuses on schema architecture and **database-schema-designer** handles ERD modeling, this skill covers the day-to-day: writing queries, optimizing performance, generating migrations, and bridging the gap between application code and database engines.

### Core Capabilities

- **Natural Language to SQL** — translate requirements into correct, performant queries
- **Schema Exploration** — introspect live databases across PostgreSQL, MySQL, SQLite, SQL Server
- **Query Optimization** — EXPLAIN analysis, index recommendations, N+1 detection, rewrite patterns
- **Migration Generation** — up/down scripts, zero-downtime strategies, rollback plans
- **ORM Integration** — Prisma, Drizzle, TypeORM, SQLAlchemy patterns and escape hatches
- **Multi-Database Support** — dialect-aware SQL with compatibility guidance

### Tools

| Script | Purpose |
|--------|---------|
| `scripts/query_optimizer.py` | Static analysis of SQL queries for performance issues |
| `scripts/migration_generator.py` | Generate migration file templates from change descriptions |
| `scripts/schema_explorer.py` | Generate schema documentation from introspection queries |

---

## Natural Language to SQL

### Translation Patterns

When converting requirements to SQL, follow this sequence:

1. **Identify entities** — map nouns to tables
2. **Identify relationships** — map verbs to JOINs or subqueries
3. **Identify filters** — map adjectives/conditions to WHERE clauses
4. **Identify aggregations** — map "total", "average", "count" to GROUP BY
5. **Identify ordering** — map "top", "latest", "highest" to ORDER BY + LIMIT

### Common Query Templates

**Top-N per group (window function)**
```sql
SELECT * FROM (
  SELECT *, ROW_NUMBER() OVER (PARTITION BY department_id ORDER BY salary DESC) AS rn
  FROM employees
) ranked WHERE rn <= 3;
```

**Running totals**
```sql
SELECT date, amount,
  SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM transactions;
```

**Gap detection**
```sql
SELECT curr.id, curr.seq_num, prev.seq_num AS prev_seq
FROM records curr
LEFT JOIN records prev ON prev.seq_num = curr.seq_num - 1
WHERE prev.id IS NULL AND curr.seq_num > 1;
```

**UPSERT (PostgreSQL)**
```sql
INSERT INTO settings (key, value, updated_at)
VALUES ('theme', 'dark', NOW())
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value, updated_at = EXCLUDED.updated_at;
```

**UPSERT (MySQL)**
```sql
INSERT INTO settings (key_name, value, updated_at)
VALUES ('theme', 'dark', NOW())
ON DUPLICATE KEY UPDATE value = VALUES(value), updated_at = VALUES(updated_at);
```

> See references/query_patterns.md for JOINs, CTEs, window functions, JSON operations, and more.

---

## Schema Exploration

### Introspection Queries

**PostgreSQL — list tables and columns**
```sql
SELECT table_name, column_name, data_type, is_nullable, column_default
FROM information_schema.columns
WHERE table_schema = 'public'
ORDER BY table_name, ordinal_position;
```

**PostgreSQL — foreign keys**
```sql
SELECT tc.table_name, kcu.column_name,
  ccu.table_name AS foreign_table, ccu.column_name AS foreign_column
FROM information_schema.table_constraints tc
JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY';
```

**MySQL — table sizes**
```sql
SELECT table_name, table_rows,
  ROUND(data_length / 1024 / 1024, 2) AS data_mb,
  ROUND(index_length / 1024 / 1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY data_length DESC;
```

**SQLite — schema dump**
```sql
SELECT name, sql FROM sqlite_master WHERE type = 'table' ORDER BY name;
```

**SQL Server — columns with types**
```sql
SELECT t.name AS table_name, c.name AS column_name,
  ty.name AS data_type, c.max_length, c.is_nullable
FROM sys.columns c
JOIN sys.tables t ON c.object_id = t.object_id
JOIN sys.types ty ON c.user_type_id = ty.user_type_id
ORDER BY t.name, c.column_id;
```

### Generating Documentation from Schema

Use `scripts/schema_explorer.py` to produce markdown or JSON documentation:

```bash
python scripts/schema_explorer.py --dialect postgres --tables all --format md
python scripts/schema_explorer.py --dialect mysql --tables users,orders --format json --json
```

---

## Query Optimization

### EXPLAIN Analysis Workflow

1. **Run EXPLAIN ANALYZE** (PostgreSQL) or **EXPLAIN FORMAT=JSON** (MySQL)
2. **Identify the costliest node** — Seq Scan on large tables, Nested Loop with high row estimates
3. **Check for missing indexes** — sequential scans on filtered columns
4. **Look for estimation errors** — planned vs actual rows divergence signals stale statistics
5. **Evaluate JOIN order** — ensure the smallest result set drives the join

### Index Recommendation Checklist

- Columns in WHERE clauses with high selectivity
- Columns in JOIN conditions (foreign keys)
- Columns in ORDER BY when combined with LIMIT
- Composite indexes matching multi-column WHERE predicates (most selective column first)
- Partial indexes for queries with constant filters (e.g., `WHERE status = 'active'`)
- Covering indexes to avoid table lookups for read-heavy queries

### Query Rewriting Patterns

| Anti-Pattern | Rewrite |
|-------------|---------|
| `SELECT * FROM orders` | `SELECT id, status, total FROM orders` (explicit columns) |
| `WHERE YEAR(created_at) = 2025` | `WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01'` (sargable) |
| Correlated subquery in SELECT | LEFT JOIN with aggregation |
| `NOT IN (SELECT ...)` with NULLs | `NOT EXISTS (SELECT 1 ...)` |
| `UNION` (dedup) when not needed | `UNION ALL` |
| `LIKE '%search%'` | Full-text search index (GIN/FULLTEXT) |
| `ORDER BY RAND()` | Application-side random sampling or `TABLESAMPLE` |

### N+1 Detection

**Symptoms:**
- Application loop that executes one query per parent row
- ORM lazy-loading related entities inside a loop
- Query log shows hundreds of identical SELECT patterns with different IDs

**Fixes:**
- Use eager loading (`include` in Prisma, `joinedload` in SQLAlchemy)
- Batch queries with `WHERE id IN (...)`
- Use DataLoader pattern for GraphQL resolvers

### Static Analysis Tool

```bash
python scripts/query_optimizer.py --query "SELECT * FROM orders WHERE status = 'pending'" --dialect postgres
python scripts/query_optimizer.py --query queries.sql --dialect mysql --json
```

> See references/optimization_guide.md for EXPLAIN plan reading, index types, and connection pooling.

---

## Migration Generation

### Zero-Downtime Migration Patterns

**Adding a column (safe)**
```sql
-- Up
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- Down
ALTER TABLE users DROP COLUMN phone;
```

**Renaming a column (expand-contract)**
```sql
-- Step 1: Add new column
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
-- Step 2: Backfill
UPDATE users SET full_name = name;
-- Step 3: Deploy app reading both columns
-- Step 4: Deploy app writing only new column
-- Step 5: Drop old column
ALTER TABLE users DROP COLUMN name;
```

**Adding a NOT NULL column (safe sequence)**
```sql
-- Step 1: Add nullable
ALTER TABLE orders ADD COLUMN region VARCHAR(50);
-- Step 2: Backfill with default
UPDATE orders SET region = 'unknown' WHERE region IS NULL;
-- Step 3: Add constraint
ALTER TABLE orders ALTER COLUMN region SET NOT NULL;
ALTER TABLE orders ALTER COLUMN region SET DEFAULT 'unknown';
```

**Index creation (non-blocking, PostgreSQL)**
```sql
CREATE INDEX CONCURRENTLY idx_orders_status ON orders (status);
```

### Data Backfill Strategies

- **Batch updates** — process in chunks of 1000-10000 rows to avoid lock contention
- **Background jobs** — run backfills asynchronously with progress tracking
- **Dual-write** — write to old and new columns during transition period
- **Validation queries** — verify row counts and data integrity after each batch

### Rollback Strategies

Every migration must have a reversible down script. For irreversible changes:

1. **Backup before execution** — `pg_dump` the affected tables
2. **Feature flags** — application can switch between old/new schema reads
3. **Shadow tables** — keep a copy of the original table during migration window

### Migration Generator Tool

```bash
python scripts/migration_generator.py --change "add email_verified boolean to users" --dialect postgres --format sql
python scripts/migration_generator.py --change "rename column name to full_name in customers" --dialect mysql --format alembic --json
```

---

## Multi-Database Support

### Dialect Differences

| Feature | PostgreSQL | MySQL | SQLite | SQL Server |
|---------|-----------|-------|--------|------------|
| UPSERT | `ON CONFLICT DO UPDATE` | `ON DUPLICATE KEY UPDATE` | `ON CONFLICT DO UPDATE` | `MERGE` |
| Boolean | Native `BOOLEAN` | `TINYINT(1)` | `INTEGER` | `BIT` |
| Auto-increment | `SERIAL` / `GENERATED` | `AUTO_INCREMENT` | `INTEGER PRIMARY KEY` | `IDENTITY` |
| JSON | `JSONB` (indexed) | `JSON` | Text (ext) | `NVARCHAR(MAX)` |
| Array | Native `ARRAY` | Not supported | Not supported | Not supported |
| CTE (recursive) | Full support | 8.0+ | 3.8.3+ | Full support |
| Window functions | Full support | 8.0+ | 3.25.0+ | Full support |
| Full-text search | `tsvector` + GIN | `FULLTEXT` index | FTS5 extension | Full-text catalog |
| LIMIT/OFFSET | `LIMIT n OFFSET m` | `LIMIT n OFFSET m` | `LIMIT n OFFSET m` | `OFFSET m ROWS FETCH NEXT n ROWS ONLY` |

### Compatibility Tips

- **Always use parameterized queries** — prevents SQL injection across all dialects
- **Avoid dialect-specific functions in shared code** — wrap in adapter layer
- **Test migrations on target engine** — `information_schema` varies between engines
- **Use ISO date format** — `'YYYY-MM-DD'` works everywhere
- **Quote identifiers** — use double quotes (SQL standard) or backticks (MySQL)

---

## ORM Patterns

### Prisma

**Schema definition**
```prisma
model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
}

model Post {
  id       Int    @id @default(autoincrement())
  title    String
  author   User   @relation(fields: [authorId], references: [id])
  authorId Int
}
```

**Migrations**: `npx prisma migrate dev --name add_user_email`
**Query API**: `prisma.user.findMany({ where: { email: { contains: '@' } }, include: { posts: true } })`
**Raw SQL escape hatch**: `prisma.$queryRaw\`SELECT * FROM users WHERE id = ${userId}\``

### Drizzle

**Schema-first definition**
```typescript
export const users = pgTable('users', {
  id: serial('id').primaryKey(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  name: text('name'),
  createdAt: timestamp('created_at').defaultNow(),
});
```

**Query builder**: `db.select().from(users).where(eq(users.email, email))`
**Migrations**: `npx drizzle-kit generate:pg` then `npx drizzle-kit push:pg`

### TypeORM

**Entity decorators**
```typescript
@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ unique: true })
  email: string;

  @OneToMany(() => Post, post => post.author)
  posts: Post[];
}
```

**Repository pattern**: `userRepo.find({ where: { email }, relations: ['posts'] })`
**Migrations**: `npx typeorm migration:generate -n AddUserEmail`

### SQLAlchemy

**Declarative models**
```python
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    email = Column(String(255), unique=True, nullable=False)
    name = Column(String(255))
    posts = relationship('Post', back_populates='author')
```

**Session management**: Always use `with Session() as session:` context manager
**Alembic migrations**: `alembic revision --autogenerate -m "add user email"`

> See references/orm_patterns.md for side-by-side comparisons and migration workflows per ORM.

---

## Data Integrity

### Constraint Strategy

- **Primary keys** — every table must have one; prefer surrogate keys (serial/UUID)
- **Foreign keys** — enforce referential integrity; define ON DELETE behavior explicitly
- **UNIQUE constraints** — for business-level uniqueness (email, slug, API key)
- **CHECK constraints** — validate ranges, enums, and business rules at the DB level
- **NOT NULL** — default to NOT NULL; make nullable only when genuinely optional

### Transaction Isolation Levels

| Level | Dirty Read | Non-Repeatable Read | Phantom Read | Use Case |
|-------|-----------|-------------------|-------------|----------|
| READ UNCOMMITTED | Yes | Yes | Yes | Never recommended |
| READ COMMITTED | No | Yes | Yes | Default for PostgreSQL, general OLTP |
| REPEATABLE READ | No | No | Yes (InnoDB: No) | Financial calculations |
| SERIALIZABLE | No | No | No | Critical consistency (billing, inventory) |

### Deadlock Prevention

1. **Consistent lock ordering** — always acquire locks in the same table/row order
2. **Short transactions** — minimize time between first lock and commit
3. **Advisory locks** — use `pg_advisory_lock()` for application-level coordination
4. **Retry logic** — catch deadlock errors and retry with exponential backoff

---

## Backup & Restore

### PostgreSQL
```bash
# Full backup
pg_dump -Fc --no-owner dbname > backup.dump
# Restore
pg_restore -d dbname --clean --no-owner backup.dump
# Point-in-time recovery: configure WAL archiving + restore_command
```

### MySQL
```bash
# Full backup
mysqldump --single-transaction --routines --triggers dbname > backup.sql
# Restore
mysql dbname < backup.sql
# Binary log for PITR: mysqlbinlog --start-datetime="2025-01-01 00:00:00" binlog.000001
```

### SQLite
```bash
# Backup (safe with concurrent reads)
sqlite3 dbname ".backup backup.db"
```

### Backup Best Practices
- **Automate** — cron or systemd timer, never manual-only
- **Test restores** — untested backups are not backups
- **Offsite copies** — S3, GCS, or separate region
- **Retention policy** — daily for 7 days, weekly for 4 weeks, monthly for 12 months
- **Monitor backup size and duration** — sudden changes signal issues

---

## Anti-Patterns

| Anti-Pattern | Problem | Fix |
|-------------|---------|-----|
| `SELECT *` | Transfers unnecessary data, breaks on schema changes | Explicit column list |
| Missing indexes on FK columns | Slow JOINs and cascading deletes | Add indexes on all foreign keys |
| N+1 queries | 1 + N round trips to database | Eager loading or batch queries |
| Implicit type coercion | `WHERE id = '123'` prevents index use | Match types in predicates |
| No connection pooling | Exhausts connections under load | PgBouncer, ProxySQL, or ORM pool |
| Unbounded queries | No LIMIT risks returning millions of rows | Always paginate |
| Storing money as FLOAT | Rounding errors | Use `DECIMAL(19,4)` or integer cents |
| God tables | One table with 50+ columns | Normalize or use vertical partitioning |
| Soft deletes everywhere | Complicates every query with `WHERE deleted_at IS NULL` | Archive tables or event sourcing |
| Raw string concatenation | SQL injection | Parameterized queries always |

---

## Cross-References

| Skill | Relationship |
|-------|-------------|
| **database-designer** | Schema architecture, normalization analysis, ERD generation |
| **database-schema-designer** | Visual ERD modeling, relationship mapping |
| **migration-architect** | Complex multi-step migration orchestration |
| **api-design-reviewer** | Ensuring API endpoints align with query patterns |
| **observability-platform** | Query performance monitoring, slow query alerts |

すべてのファイル

0件のファイル

sql-database-assistantをインストール

スキルファイルをダウンロードし、.claude/skills/ ディレクトリに解凍してください。

ZIPをダウンロード

リポジトリをクローンし、スキルファイルをプロジェクトにコピーしてください。

git clone https://github.com/alirezarezvani/claude-skills/tree/main/engineering/skills/sql-database-assistant # Copy SKILL.md to your .claude/skills/ directory

コピー コピー
クイックセットアップ: スキルフォルダを .claude/skills/ にコピーしてください。 Claude が自動的にそのスキルを検出して使用します。

関連スキル

microservices-patterns
更新された時間 2026年6月29日
jpa-patterns
更新された時間 2026年6月30日
fabric-lakehouse
更新された時間 2026年6月30日
prisma-expert
更新された時間 2026年6月29日
OR