sql-database-assistant
alirezarezvani/claude-skills
將自然語言轉換為 SQL 查詢、優化資料庫效能、產生遷移指令、探索資料結構,並在 PostgreSQL、MySQL、SQLite 及 SQL Server 環境中使用 ORM。
...展開全部SQL 資料庫助理 - 強大層級技能
概述
這是資料庫設計的實務輔助技能。資料庫設計師專注於模式架構,而資料庫模式設計師則負責 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
- 識別排序— 將「前 N 筆」、「最新」、「最高」對應至 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)
- 識別成本最高的節點— 大型資料表上的順序掃描(Seq Scan),以及行數估計值偏高的嵌套迴圈(Nested Loop)
- 檢查是否缺少索引— 篩選欄位出現順序掃描
- 尋找估計錯誤— 計畫行數與實際行數的偏差可能表示統計資料已過時
- 評估 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(dedup) |
UNION ALL |
LIKE '%search%' |
全文搜尋索引 (GIN/FULLTEXT) |
ORDER BY RAND() |
應用程式端的隨機抽樣或TABLESAMPLE |
N+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 "在 users 表中新增 boolean 型別的 email_verified 欄位" --dialect postgres --format sql
python scripts/migration_generator.py --change "將 customers 表中的 column_name 欄位重命名為 full_name" --dialect mysql --format alembic --json
多資料庫支援
方言差異
| 功能 | PostgreSQL | MySQL | SQLite | SQL Server |
|---|---|---|---|---|
| UPSERT | 若發生衝突則執行更新 |
若出現重複鍵則更新 |
若發生衝突則執行更新 |
合併 |
| 布林值 | 原生布林值 |
TINYINT(1) |
整數 |
BIT |
| 自動遞增 | 序列號/自動產生 |
AUTO_INCREMENT |
整數 主鍵 |
自增 |
| JSON | JSONB(已建立索引) |
JSON |
文字 (擴展) | NVARCHAR(MAX) |
| 陣列 | 原生陣列 |
不支援 | 不支援 | 不支援 |
| 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 列 僅擷取後續 n 列 |
相容性提示
- 請務必使用參數化查詢— 可在所有方言中防止 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() 作為會話:上下文管理器
Alembic 遷移:alembic revision --autogenerate -m "新增使用者電子郵件"
請參閱 references/orm_patterns.md,了解各 ORM 的並列比較與遷移工作流程。
資料完整性
約束策略
- 主鍵— 每張資料表都必須有一個;建議使用代理鍵(序列號/UUID)
- 外鍵— 強制執行參照完整性;應明確定義 ON DELETE 行為
- UNIQUE 約束— 用於業務層級的唯一性(電子郵件、slug、API 金鑰)
- CHECK 約束— 在資料庫層級驗證範圍、枚舉及業務規則
- NOT NULL— 預設為 NOT NULL;僅在真正可選時才允許為 NULL
交易隔離級別
| 層級 | 髒讀 | 不可重複讀取 | 幻讀 | 使用案例 |
|---|---|---|---|---|
| 未提交讀取 | 是 | 是 | 是 | 絕不建議 |
| 已提交讀取 | 否 | 是 | 是 | PostgreSQL 的預設設定,一般 OLTP |
| 可重複讀取 | 否 | 否 | 是(InnoDB:否) | 財務計算 |
| 可序列化 | 否 | 否 | 否 | 關鍵一致性(計費、庫存) |
死鎖預防
- 一致的鎖定順序— 總是依照相同的表格/行順序取得鎖定
- 短交易— 將首次取得鎖與提交之間的間隔時間降至最低
- 諮詢鎖— 使用
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 資料庫名稱 ".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)或整數分 |
| 神級資料表 | 單一資料表含 50 多個欄位 | 進行正規化或採用垂直分區 |
| 處處使用軟刪除 | 每個查詢都因WHERE deleted_at IS NULL而變得複雜 |
歸檔資料表或事件來源 |
| 原始字串拼接 | SQL 注入 | 始終使用參數化查詢 |
交叉引用
| 技能 | 關聯 |
|---|---|
| 資料庫設計師 | 模式架構、正規化分析、ERD 生成 |
| 資料庫模式設計師 | 視覺化 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
複製





首頁
