sql-database-assistant
alirezarezvani/claude-skills
自然言語をSQLクエリに変換し、データベースのパフォーマンスを最適化し、マイグレーションを生成し、スキーマを調査し、PostgreSQL、MySQL、SQLite、SQL Serverの各データベースでORMを活用します。
...すべて拡張します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に変換する際は、以下の手順に従ってください:
- エンティティを特定する— 名詞をテーブルにマッピングする
- 関係を特定する— 動詞をJOINまたはサブクエリにマッピングする
- フィルタを特定する— 形容詞や条件をWHERE句にマッピングする
- 集計を特定する— 「合計」、「平均」、「件数」を GROUP BY にマッピングする
- 並べ替えを特定する— 「上位」、「最新」、「最高」を 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 分析のワークフロー
- EXPLAIN ANALYZE(PostgreSQL) またはEXPLAIN FORMAT=JSON(MySQL)を実行します
- コストが最も高いノードを特定する— 大規模テーブルでのシーケンシャルスキャン、行数の推定値が高いネストループ
- インデックスの欠落を確認する— フィルタリングされた列でのシーケンシャルスキャン
- 推定値の誤りを確認する— 計画行数と実際行数の乖離は、統計情報の古さを示唆している
- 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行単位で処理する
- バックグラウンドジョブ— 進行状況を追跡しながらバックフィルを非同期で実行
- デュアル書き込み— 移行期間中は、旧列と新列の両方に書き込む
- 検証クエリ— 各バッチ処理後に、行数とデータの整合性を確認する
ロールバック戦略
すべての移行には、元に戻せるダウンスクリプトが必要です。元に戻せない変更については:
- 実行前のバックアップ— 影響を受けるテーブルを
pg_dump でバックアップ - 機能フラグ— アプリケーションで旧スキーマと新スキーマの読み取りを切り替え可能
- シャドウテーブル— 移行期間中は元のテーブルのコピーを保持する
マイグレーション生成ツール
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 | いいえ | いいえ | いいえ | クリティカルな一貫性(請求、在庫) |
デッドロック防止
- 一貫性のあるロック順序— 常に同じテーブル/行の順序でロックを取得する
- 短いトランザクション— 最初のロックからコミットまでの時間を最小限に抑える
- アドバイザリロック— アプリケーションレベルの調整には
pg_advisory_lock()を使用する - 再試行ロジック— デッドロックエラーを検出し、指数関数的バックオフを用いて再試行する
バックアップと復元
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エンドポイントがクエリパターンと整合していることを確認 |
| 可観測性プラットフォーム | クエリパフォーマンスの監視、低速クエリのアラート |
---
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
コピー





家
