选项
首页首页 Skill 数据库管理 sql-database-assistant

sql-database-assistant

alirezarezvani/claude-skills alirezarezvani/claude-skills

将自然语言转换为 SQL 查询、优化数据库性能、生成迁移脚本、探索模式,并在 PostgreSQL、MySQL、SQLite 和 SQL Server 上使用 ORM。

...展开全部
1
更新时间 2026-09-02

SQL 数据库助理——POWERFUL 级技能

概述

这是数据库设计的运维伴侣。数据库设计师专注于模式架构,而数据库模式设计师则负责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. 确定排序— 将“top”、“latest”、“highest”映射到 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. 识别成本最高的节点——对大型表的顺序扫描(Seq Scan),或行数估计值较高的嵌套循环(Nested Loop)
  3. 检查是否缺少索引— 过滤列上的顺序扫描
  4. 查找估算误差——计划行数与实际行数存在偏差表明统计信息已过时
  5. 评估 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 问题检测

症状:

  • 应用程序循环,针对每行父行执行一次查询
  • 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:添加可为空列
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 "在 users 表中添加 boolean 类型的 email_verified 字段" --dialect postgres --format sql
python scripts/migration_generator.py --change "将 customers 表中的 name 列重命名为 full_name" --dialect mysql --format alembic --json

多数据库支持

方言差异

功能 PostgreSQL MySQL SQLite SQL Server
UPSERT ON CONFLICT DO UPDATE ON DUPLICATE KEY UPDATE ON CONFLICT DO UPDATE 合并
布尔值 本机布尔值 TINYINT(1) INTEGER 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 查询 APIprisma.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;仅在真正可选时才允许为空

事务隔离级别

级别 脏读 不可重复读 幻读 用例
未提交读取 绝不推荐
已提交读取 PostgreSQL 的默认设置,通用 OLTP
可重复读 是(InnoDB:否) 财务计算
可串行化 关键一致性(计费、库存)

死锁预防

  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 * 传输不必要的数据,在模式变更时会导致故障 显式列列表
外键列上缺少索引 连接操作缓慢且级联删除 在所有外键上添加索引
N+1查询 与数据库进行 1 + N 次往返 预加载或批量查询
隐式类型转换 WHERE id = '123'会阻止索引的使用 谓词中的类型匹配
未使用连接池 高负载下耗尽连接 PgBouncer、ProxySQL 或 ORM 连接池
无限制的查询 未使用 LIMIT 可能导致返回数百万行 始终进行分页
将货币值存储为 FLOAT 类型 舍入误差 使用DECIMAL(19,4)或整数表示分
“神表” 一个包含 50 多个列的表 进行规范化或使用垂直分区
到处都使用软删除 每个查询都因WHERE deleted_at IS NULL而变得复杂 归档表或事件溯源
原始字符串拼接 SQL注入 始终使用参数化查询

交叉引用

技能 关系
数据库设计员 模式架构、规范化分析、ER图生成
数据库模式设计师 可视化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-06-29
jpa-patterns
更新时间 2026-06-30
fabric-lakehouse
更新时间 2026-06-30
prisma-expert
更新时间 2026-06-29
OR