opção
LarLar Skill Gerenciamento de banco de dados sql-database-assistant

sql-database-assistant

alirezarezvani/claude-skills alirezarezvani/claude-skills

Traduza linguagem natural em consultas SQL, otimize o desempenho do banco de dados, gere migrações, explore esquemas e trabalhe com ORMs no PostgreSQL, MySQL, SQLite e SQL Server.

...Expandir tudo
1
Tempo atualizado 2 de Setembro de 2026

Assistente de Banco de Dados SQL - Habilidade de Nível AVANÇADO

Visão geral

O companheiro operacional do projeto de bancos de dados. Enquanto o projetista de bancos de dados se concentra na arquitetura do esquema e o projetista de esquemas de bancos de dados lida com a modelagem ERD, esta competência abrange o dia a dia: escrever consultas, otimizar o desempenho, gerar migrações e fazer a ponte entre o código da aplicação e os mecanismos de banco de dados.

Principais recursos

  • Linguagem Natural para SQL — traduzir requisitos em consultas corretas e de alto desempenho
  • Exploração de esquema — analisa bancos de dados ativos em PostgreSQL, MySQL, SQLite e SQL Server
  • Otimização de consultas — análise EXPLAIN, recomendações de índices, detecção de N+1, padrões de reescrita
  • Geração de migrações — scripts de atualização/reversão, estratégias sem tempo de inatividade, planos de reversão
  • Integração com ORM — padrões e soluções alternativas para Prisma, Drizzle, TypeORM e SQLAlchemy
  • Suporte a múltiplos bancos de dados — SQL sensível a dialetos com orientações de compatibilidade

Ferramentas

Script Finalidade
scripts/query_optimizer.py Análise estática de consultas SQL para identificar problemas de desempenho
scripts/migration_generator.py Geração de modelos de arquivos de migração a partir de descrições de alterações
scripts/schema_explorer.py Gera documentação de esquema a partir de consultas de introspecção

Linguagem natural para SQL

Padrões de tradução

Ao converter requisitos para SQL, siga esta sequência:

  1. Identifique entidades — mapeie substantivos para tabelas
  2. Identifique relações — mapeie verbos para JOINs ou subconsultas
  3. Identifique filtros — mapeie adjetivos/condições para cláusulas WHERE
  4. Identifique agregações — mapeie “total”, “média”, “contagem” para GROUP BY
  5. Identifique a ordenação — mapeie “top”, “mais recente”, “mais alto” para ORDER BY + LIMIT

Modelos comuns de consulta

Top-N por grupo (função de janela)

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

Totais acumulados

SELECT data, valor,
  SUM(valor) OVER (ORDER BY data ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS total_acumulado
FROM transações;

Detecção de lacunas

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 configurações (chave, valor, atualizado_em)
VALUES ('tema', 'escuro', NOW())
ON CONFLICT (chave) DO UPDATE SET valor = EXCLUÍDO.valor, atualizado_em = EXCLUÍDO.atualizado_em;

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);

Consulte references/query_patterns.md para obter informações sobre JOINs, CTEs, funções de janela, operações JSON e muito mais.

Exploração do esquema

Consultas de introspecção

PostgreSQL — listar tabelas e colunas

SELECT nome_da_tabela, nome_da_coluna, tipo_de_dados, é_nulo, valor_padrão_da_coluna
FROM information_schema.columns
WHERE esquema_da_tabela = 'public'
ORDER BY nome_da_tabela, posição_ordinal;

PostgreSQL — chaves estrangeiras

SELECT tc.nome_da_tabela, kcu.nome_da_coluna,
  ccu.nome_da_tabela AS tabela_estranha, ccu.nome_da_coluna AS coluna_estranha
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 — tamanhos das tabelas

SELECT nome_da_tabela, linhas_da_tabela,
  ROUND(comprimento_dos_dados / 1024 / 1024, 2) AS dados_mb,
  ROUND(comprimento_do_índice / 1024 / 1024, 2) AS índice_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY data_length DESC;

SQLite — dump do esquema

SELECT nome, sql FROM sqlite_master WHERE tipo = 'tabela' ORDER BY nome;

SQL Server — colunas com tipos

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;

Gerando documentação a partir do esquema

Use scripts/schema_explorer.py para gerar documentação em Markdown ou 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

Otimização de consultas

Fluxo de trabalho da análise EXPLAIN

  1. Execute EXPLAIN ANALYZE (PostgreSQL) ou EXPLAIN FORMAT=JSON (MySQL)
  2. Identifique o nó mais dispendioso — Seq Scan em tabelas grandes, Nested Loop com estimativas elevadas de linhas
  3. Verifique se há índices ausentes — varreduras sequenciais em colunas filtradas
  4. Procure por erros de estimativa — divergências entre o número de linhas planejado e o real indicam estatísticas desatualizadas
  5. Avalie a ordem do JOIN — certifique-se de que o menor conjunto de resultados conduza a junção

Lista de verificação de recomendações de índices

  • Colunas nas cláusulas WHERE com alta seletividade
  • Colunas nas condições JOIN (chaves estrangeiras)
  • Colunas na cláusula ORDER BY quando combinadas com LIMIT
  • Índices compostos que correspondem a predicados WHERE com várias colunas (coluna mais seletiva em primeiro lugar)
  • Índices parciais para consultas com filtros constantes (por exemplo, WHERE status = 'ativo')
  • Índices abrangentes para evitar consultas à tabela em consultas com grande volume de leitura

Padrões de reescrita de consultas

Antipadrão Reescrita
SELECT * FROM pedidos SELECT id, status, total FROM orders (colunas explícitas)
WHERE YEAR(created_at) = 2025 WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01' (sargável)
Subconsulta correlacionada em SELECT LEFT JOIN com agregação
NOT IN (SELECT ...) com valores NULL NOT EXISTS (SELECT 1 ...)
UNION (dedup) quando desnecessário UNION ALL
LIKE '%search%' Índice de pesquisa de texto completo (GIN/FULLTEXT)
ORDER BY RAND() Amostragem aleatória no lado do aplicativo ou TABLESAMPLE

Detecção de N+1

Sintomas:

  • Loop de aplicativo que executa uma consulta por linha pai
  • Carregamento preguiçoso (lazy-loading) de entidades relacionadas pelo ORM dentro de um loop
  • O log de consultas mostra centenas de padrões SELECT idênticos com IDs diferentes

Soluções:

  • Use carregamento antecipado (include no Prisma, joinedload no SQLAlchemy)
  • Agrupar consultas com WHERE id IN (...)
  • Use o padrão DataLoader para resolvedores GraphQL

Ferramenta de análise estática

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

Consulte references/optimization_guide.md para obter informações sobre a leitura do plano EXPLAIN, tipos de índice e pool de conexões.

Geração de migração

Padrões de migração sem tempo de inatividade

Adicionando uma coluna (seguro)

-- Para cima
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- Para baixo
ALTER TABLE users DROP COLUMN phone;

Renomeação de uma coluna (expandir-contrair)

-- Etapa 1: Adicionar nova coluna
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
-- Etapa 2: Preenchimento retroativo
UPDATE users SET full_name = name;
-- Etapa 3: Implantar o aplicativo que lê ambas as colunas
-- Etapa 4: Implantar o aplicativo que grava apenas na nova coluna
-- Etapa 5: Excluir a coluna antiga
ALTER TABLE users DROP COLUMN name;

Adicionando uma coluna NOT NULL (sequência segura)

-- Etapa 1: Adicionar coluna que pode conter NUL
ALTER TABLE orders ADD COLUMN region VARCHAR(50);
-- Etapa 2: Preenchimento retroativo com valor padrão
UPDATE orders SET region = 'unknown' WHERE region IS NULL;
-- Etapa 3: Adicionar restrição
ALTER TABLE orders ALTER COLUMN region SET NOT NULL;
ALTER TABLE orders ALTER COLUMN region SET DEFAULT 'unknown';

Criação de índice (sem bloqueio, PostgreSQL)

CREATE INDEX CONCURRENTLY idx_orders_status ON orders (status);

Estratégias de preenchimento de dados

  • Atualizações em lote — processe em blocos de 1.000 a 10.000 linhas para evitar conflitos de bloqueio
  • Tarefas em segundo plano — executam o preenchimento de dados de forma assíncrona com acompanhamento do progresso
  • Gravação dupla — grava em colunas antigas e novas durante o período de transição
  • Consultas de validação — verifique o número de linhas e a integridade dos dados após cada lote

Estratégias de reversão

Toda migração deve ter um script reversível. Para alterações irreversíveis:

  1. Faça backup antes da execução — utilize o ` pg_dump ` nas tabelas afetadas
  2. Sinalizadores de recurso — o aplicativo pode alternar entre leituras do esquema antigo e do novo
  3. Tabelas-sombra — mantenha uma cópia da tabela original durante o período de migração

Ferramenta Geradora de Migração

python scripts/migration_generator.py --change "adicionar o booleano email_verified à tabela users" --dialect postgres --format sql
python scripts/migration_generator.py --change "renomear a coluna name para full_name em customers" --dialect mysql --format alembic --json

Suporte a múltiplos bancos de dados

Diferenças entre dialetos

Recurso PostgreSQL MySQL SQLite SQL Server
UPSERT EM CASO DE CONFLITO, ATUALIZAR EM CASO DE CHAVE DUPLICADA, ATUALIZAR EM CASO DE CONFLITO, ATUALIZAR MERGE
Booleano BOOLEANO nativo TINYINT(1) INTEGER BIT
Autoincremento SÉRIE / GERADO AUTO_INCREMENT INTEGER CHAVE PRIMÁRIA IDENTIDADE
JSON JSONB (indexado) JSON Texto (ext) NVARCHAR(MAX)
Matriz Matriz nativa Não suportado Não suportado Não suportado
CTE (recursivo) Suporte total 8.0+ 3.8.3+ Suporte total
Funções de janela Suporte total 8.0+ 3.25.0+ Suporte completo
Pesquisa de texto completo tsvector + GIN ÍndiceFULLTEXT Extensão FTS5 Catálogo de texto completo
LIMIT/OFFSET LIMIT n OFFSET m LIMIT n OFFSET m LIMIT n OFFSET m DESLOCAR m LINHAS, RETIRAR SOMENTE AS PRÓXIMAS n LINHAS

Dicas de compatibilidade

  • Sempre use consultas parametrizadas — isso evita injeção de SQL em todos os dialetos
  • Evite funções específicas de dialeto em código compartilhado — envolva-as em uma camada de adaptador
  • Teste as migrações no mecanismo de destinoo `information_schema` varia entre os mecanismos
  • Use o formato de data ISO'AAAA-MM-DD' funciona em todos os lugares
  • Coloque os identificadores entre aspas — use aspas duplas (padrão SQL) ou crases (MySQL)

Padrões ORM

Prisma

Definição do esquema

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
}

Migrações: npx prisma migrate dev --name add_user_email API de consulta: prisma.user.findMany({ where: { email: { contains: '@' } }, include: { posts: true } }) Escape para SQL bruto: prisma.$queryRaw\SELECT * FROM users WHERE id = ${userId}``

Drizzle

Definição “schema-first”

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

Construtor de consultas: db.select().from(users).where(eq(users.email, email)) Migrações: npx drizzle-kit generate:pg e, em seguida, npx drizzle-kit push:pg

TypeORM

Decoradores de entidade

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

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

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

Padrão de repositório: userRepo.find({ where: { email }, relations: ['posts'] }) Migrações: npx typeorm migration:generate -n AddUserEmail

SQLAlchemy

Modelos declarativos

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')

Gerenciamento de sessão: Sempre use with Session() como gerenciador de contexto Migrações Alembic: alembic revision --autogenerate -m "adicionar e-mail do usuário"

Consulte references/orm_patterns.md para comparações lado a lado e fluxos de trabalho de migração por ORM.

Integridade dos dados

Estratégia de restrições

  • Chaves primárias — toda tabela deve ter uma; dê preferência a chaves substitutas (serial/UUID)
  • Chaves estrangeiras — imponha a integridade referencial; defina explicitamente o comportamento ON DELETE
  • Restrições UNIQUE — para exclusividade no âmbito dos negócios (e-mail, slug, chave de API)
  • Restrições CHECK — validam intervalos, enums e regras de negócios no nível do banco de dados
  • NOT NULL — use NOT NULL como padrão; torne nulo apenas quando for realmente opcional

Níveis de isolamento de transação

Nível Leitura suja Leitura não repetível Leitura fantasma Caso de uso
LEITURA NÃO CONFIRMADA Sim Sim Sim Nunca recomendado
LEITURA CONFIRMADA Não Sim Sim Padrão para PostgreSQL, OLTP geral
LEITURA REPETÍVEL Não Não Sim (InnoDB: Não) Cálculos financeiros
SERIALIZÁVEL Não Não Não Consistência crítica (faturamento, estoque)

Prevenção de impasse

  1. Ordenação consistente de bloqueios — sempre adquirir bloqueios na mesma ordem de tabela/linha
  2. Transações curtas — minimize o tempo entre o primeiro bloqueio e o commit
  3. Bloqueios consultivos — use pg_advisory_lock() para coordenação no nível do aplicativo
  4. Lógica de repetição — detectar erros de impasse e repetir a tentativa com recuo exponencial

Backup e restauração

PostgreSQL

# Backup completo
pg_dump -Fc --no-owner dbname > backup.dump
# Restauração
pg_restore -d dbname --clean --no-owner backup.dump
# Recuperação em um ponto no tempo: configure o arquivamento WAL + restore_command

MySQL

# Backup completo
mysqldump --single-transaction --routines --triggers dbname > backup.sql
# Restauração
mysql dbname < backup.sql
# Log binário para PITR: mysqlbinlog --start-datetime="2025-01-01 00:00:00" binlog.000001

SQLite

# Backup (seguro com leituras simultâneas)
sqlite3 dbname ".backup backup.db"

Melhores práticas de backup

  • Automatize — use cron ou o temporizador do systemd; nunca faça apenas manualmente
  • Teste as restaurações — backups não testados não são backups
  • Cópias externas — S3, GCS ou região separada
  • Política de retenção — diariamente por 7 dias, semanalmente por 4 semanas, mensalmente por 12 meses
  • Monitore o tamanho e a duração do backup — mudanças repentinas indicam problemas

Antipadrões

Antipadrão Problema Solução
SELECT * Transfere dados desnecessários, falha em caso de alterações no esquema Lista explícita de colunas
Falta de índices nas colunas de chave estrangeira JOINs lentos e exclusões em cascata Adicione índices em todas as chaves estrangeiras
Consultas N+1 1 + N idas e voltas ao banco de dados Carregamento antecipado ou consultas em lote
Coerção implícita de tipos WHERE id = '123' impede o uso do índice Tipos de correspondência em predicados
Sem pool de conexões Esgota as conexões sob carga PgBouncer, ProxySQL ou pool ORM
Consultas sem limite A ausência de LIMIT pode resultar no retorno de milhões de linhas Sempre paginar
Armazenamento de valores monetários como FLOAT Erros de arredondamento Use DECIMAL(19,4) ou centavos inteiros
Tabelas “divinas” Uma tabela com mais de 50 colunas Normalize ou use particionamento vertical
Exclusões temporárias em todos os lugares Complica todas as consultas com WHERE deleted_at IS NULL Tabelas de arquivo ou event sourcing
Concatenação de strings brutas Injeção de SQL Consultas parametrizadas sempre

Referências cruzadas

Habilidade Relação
projetista de banco de dados Arquitetura de esquema, análise de normalização, geração de ERD
projetista de esquema de banco de dados Modelagem visual de ERD, mapeamento de relações
arquiteto de migração Orquestração de migrações complexas em várias etapas
revisor-de-projeto-de-API Garantia de que os endpoints da API estejam alinhados com os padrões de consulta
observability-platform Monitoramento do desempenho de consultas e alertas de consultas lentas
Ver no 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 |

Todos os arquivos

0 arquivos

Instalar sql-database-assistant

Baixe e descompacte os arquivos de habilidades no diretório .claude/skills/.

Baixar ZIP

Clone o repositório e copie os arquivos da habilidade para o seu projeto.

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

Copiar Copiar
Configuração rápida: Copie a pasta da habilidade para .claude/skills/ O Claude detectará e utilizará automaticamente a habilidade

Habilidades relacionadas

microservices-patterns
Tempo atualizado 29 de Junho de 2026
jpa-patterns
Tempo atualizado 30 de Junho de 2026
fabric-lakehouse
Tempo atualizado 30 de Junho de 2026
prisma-expert
Tempo atualizado 29 de Junho de 2026
OR