| name | sql-database-assistant |
| description | Use quando o usuário pede para escrever queries SQL, otimizar desempenho de banco de dados, gerar migrações, explorar esquemas de banco de dados, ou trabalhar com ORMs como Prisma, Drizzle, TypeORM ou SQLAlchemy. |
| agents | ["claude-code"] |
SQL Database Assistant - Skill de Nível PODEROSO
Visão Geral
O companheiro operacional para design de banco de dados. Enquanto database-designer foca na arquitetura de esquemas e database-schema-designer lida com modelagem ERD, esta skill cobre o dia a dia: escrever queries, otimizar desempenho, gerar migrações e fazer a ponte entre o código da aplicação e os motores de banco de dados.
Capacidades Principais
- Linguagem Natural para SQL — traduzir requisitos em queries corretas e performáticas
- Exploração de Esquema — introspectar bancos de dados ao vivo em PostgreSQL, MySQL, SQLite, SQL Server
- Otimização de Queries — análise EXPLAIN, recomendações de índice, detecção de N+1, padrões de reescrita
- Geração de Migração — scripts up/down, estratégias de zero-downtime, planos de rollback
- Integração com ORM — padrões e escapes hatch para Prisma, Drizzle, TypeORM, SQLAlchemy
- Suporte Multi-Banco — SQL com awareness de dialeto e orientação de compatibilidade
Ferramentas
| Script | Propósito |
|---|
scripts/query_optimizer.py | Análise estática de queries SQL para problemas de desempenho |
scripts/migration_generator.py | Gerar templates de arquivos de migração a partir de descrições de mudanças |
scripts/schema_explorer.py | Gerar documentação de esquema a partir de queries de introspecção |
Linguagem Natural para SQL
Padrões de Tradução
Ao converter requisitos para SQL, siga esta sequência:
- Identificar entidades — mapear substantivos para tabelas
- Identificar relacionamentos — mapear verbos para JOINs ou subqueries
- Identificar filtros — mapear adjetivos/condições para cláusulas WHERE
- Identificar agregações — mapear "total", "média", "contagem" para GROUP BY
- Identificar ordenação — mapear "top", "mais recente", "mais alto" para ORDER BY + LIMIT
Templates Comuns de Queries
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 date, amount,
SUM(amount) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS running_total
FROM transactions;
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 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);
Consulte references/query_patterns.md para JOINs, CTEs, funções de janela, operações JSON e muito mais.
Exploração de Esquema
Queries de Introspecção
PostgreSQL — listar tabelas e colunas
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 — chaves estrangeiras
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 — tamanhos de tabela
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 — dump de esquema
SELECT name, sql FROM sqlite_master WHERE type = 'table' ORDER BY name;
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 do Esquema
Use scripts/schema_explorer.py para produzir 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 Queries
Fluxo de Trabalho de Análise EXPLAIN
- Executar EXPLAIN ANALYZE (PostgreSQL) ou EXPLAIN FORMAT=JSON (MySQL)
- Identificar o nó mais custoso — Seq Scan em tabelas grandes, Nested Loop com estimativas altas de linhas
- Verificar índices ausentes — varreduras sequenciais em colunas filtradas
- Procurar erros de estimativa — divergência entre linhas planejadas e reais sinaliza estatísticas desatualizadas
- Avaliar ordem de JOIN — garantir que o menor conjunto de resultados direcione o join
Lista de Verificação de Recomendação de Índice
- Colunas em cláusulas WHERE com alta seletividade
- Colunas em condições JOIN (chaves estrangeiras)
- Colunas em ORDER BY quando combinadas com LIMIT
- Índices compostos correspondendo a predicados WHERE de múltiplas colunas (coluna mais seletiva primeiro)
- Índices parciais para queries com filtros constantes (ex.:
WHERE status = 'active')
- Índices de cobertura para evitar lookups de tabela em queries de leitura intensa
Padrões de Reescrita de Queries
| Anti-Padrão | Reescrita |
|---|
SELECT * FROM orders | 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) |
| Subquery correlacionada no SELECT | LEFT JOIN com agregação |
NOT IN (SELECT ...) com NULLs | NOT EXISTS (SELECT 1 ...) |
UNION (dedup) quando não necessário | UNION ALL |
LIKE '%search%' | Índice de busca full-text (GIN/FULLTEXT) |
ORDER BY RAND() | Amostragem aleatória no lado da aplicação ou TABLESAMPLE |
Detecção de N+1
Sintomas:
- Loop de aplicação que executa uma query por linha pai
- Carregamento lazy de ORM de entidades relacionadas dentro de um loop
- Log de queries mostra centenas de padrões SELECT idênticos com IDs diferentes
Correções:
- Usar carregamento eager (
include no Prisma, joinedload no SQLAlchemy)
- Queries em lote com
WHERE id IN (...)
- Usar padrão DataLoader para resolvers 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 leitura de planos EXPLAIN, tipos de índice e pooling de conexão.
Geração de Migração
Padrões de Migração com Zero Downtime
Adicionar uma coluna (seguro)
ALTER TABLE users ADD COLUMN phone VARCHAR(20);
ALTER TABLE users DROP COLUMN phone;
Renomear uma coluna (expand-contract)
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
UPDATE users SET full_name = name;
ALTER TABLE users DROP COLUMN name;
Adicionar uma coluna NOT NULL (sequência segura)
ALTER TABLE orders ADD COLUMN region VARCHAR(50);
UPDATE orders SET region = 'unknown' WHERE region IS NULL;
ALTER TABLE orders ALTER COLUMN region SET NOT NULL;
ALTER TABLE orders ALTER COLUMN region SET DEFAULT 'unknown';
Criação de índice (não-bloqueante, PostgreSQL)
CREATE INDEX CONCURRENTLY idx_orders_status ON orders (status);
Estratégias de Preenchimento de Dados
- Atualizações em lote — processar em blocos de 1000-10000 linhas para evitar contenção de lock
- Jobs em background — executar preenchimentos de forma assíncrona com rastreamento de progresso
- Escrita dupla — escrever em colunas antigas e novas durante o período de transição
- Queries de validação — verificar contagens de linhas e integridade de dados após cada lote
Estratégias de Rollback
Toda migração deve ter um script down reversível. Para mudanças irreversíveis:
- Backup antes da execução —
pg_dump das tabelas afetadas
- Flags de funcionalidade — a aplicação pode alternar entre leituras de esquema antigo/novo
- Tabelas sombra — manter uma cópia da tabela original durante a janela de migração
Ferramenta de Geração de Migração
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
Suporte Multi-Banco
Diferenças de Dialeto
| Funcionalidade | PostgreSQL | MySQL | SQLite | SQL Server |
|---|
| UPSERT | ON CONFLICT DO UPDATE | ON DUPLICATE KEY UPDATE | ON CONFLICT DO UPDATE | MERGE |
| Boolean | BOOLEAN nativo | TINYINT(1) | INTEGER | BIT |
| Auto-incremento | SERIAL / GENERATED | AUTO_INCREMENT | INTEGER PRIMARY KEY | IDENTITY |
| JSON | JSONB (indexado) | JSON | Text (ext) | NVARCHAR(MAX) |
| Array | ARRAY nativo | 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 total |
| Busca full-text | tsvector + GIN | Índice FULLTEXT | Extensão FTS5 | Catálogo full-text |
| LIMIT/OFFSET | LIMIT n OFFSET m | LIMIT n OFFSET m | LIMIT n OFFSET m | OFFSET m ROWS FETCH NEXT n ROWS ONLY |
Dicas de Compatibilidade
- Sempre use queries parametrizadas — previne SQL injection em todos os dialetos
- Evite funções específicas de dialeto no código compartilhado — envolva em camada adaptadora
- Testar migrações no motor de destino —
information_schema varia entre motores
- Use formato de data ISO —
'YYYY-MM-DD' funciona em todos
- Aspas em identificadores — use aspas duplas (padrão SQL) ou backticks (MySQL)
Padrões de ORM
Prisma
Definição de 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 Query: prisma.user.findMany({ where: { email: { contains: '@' } }, include: { posts: true } })
Escape hatch 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(),
});
Query builder: db.select().from(users).where(eq(users.email, email))
Migrações: npx drizzle-kit generate:pg depois 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() as session: como gerenciador de contexto
Migrações Alembic: alembic revision --autogenerate -m "add user email"
Consulte references/orm_patterns.md para comparações lado a lado e fluxos de trabalho de migração por ORM.
Integridade de Dados
Estratégia de Restrições
- Chaves primárias — toda tabela deve ter uma; prefira chaves substitutas (serial/UUID)
- Chaves estrangeiras — aplicar integridade referencial; definir comportamento ON DELETE explicitamente
- Restrições UNIQUE — para unicidade de nível de negócio (email, slug, chave de API)
- Restrições CHECK — validar intervalos, enums e regras de negócio no nível do BD
- NOT NULL — padrão para NOT NULL; tornar nullable apenas quando genuinamente opcional
Níveis de Isolamento de Transação
| Nível | Leitura Suja | Leitura Não-Repetível | Leitura Fantasma | Caso de Uso |
|---|
| READ UNCOMMITTED | Sim | Sim | Sim | Nunca recomendado |
| READ COMMITTED | Não | Sim | Sim | Padrão para PostgreSQL, OLTP geral |
| REPEATABLE READ | Não | Não | Sim (InnoDB: Não) | Cálculos financeiros |
| SERIALIZABLE | Não | Não | Não | Consistência crítica (faturamento, estoque) |
Prevenção de Deadlock
- Ordem consistente de lock — sempre adquirir locks na mesma ordem de tabela/linha
- Transações curtas — minimizar o tempo entre o primeiro lock e o commit
- Locks Advisory — usar
pg_advisory_lock() para coordenação no nível da aplicação
- Lógica de retry — capturar erros de deadlock e fazer retry com backoff exponencial
Backup e Restauração
PostgreSQL
pg_dump -Fc --no-owner dbname > backup.dump
pg_restore -d dbname --clean --no-owner backup.dump
MySQL
mysqldump --single-transaction --routines --triggers dbname > backup.sql
mysql dbname < backup.sql
SQLite
sqlite3 dbname ".backup backup.db"
Melhores Práticas de Backup
- Automatizar — cron ou systemd timer, nunca apenas manual
- Testar restaurações — backups não testados não são backups
- Cópias offsite — S3, GCS ou região separada
- Política de retenção — diário por 7 dias, semanal por 4 semanas, mensal por 12 meses
- Monitorar tamanho e duração do backup — mudanças repentinas sinalizam problemas
Anti-Padrões
| Anti-Padrão | Problema | Correção |
|---|
SELECT * | Transfere dados desnecessários, quebra em mudanças de esquema | Lista explícita de colunas |
| Índices ausentes em colunas FK | JOINs lentos e exclusões em cascata | Adicionar índices em todas as chaves estrangeiras |
| Queries N+1 | 1 + N round trips para o banco de dados | Carregamento eager ou queries em lote |
| Coerção de tipo implícita | WHERE id = '123' impede uso de índice | Corresponder tipos em predicados |
| Sem pooling de conexão | Esgota conexões sob carga | PgBouncer, ProxySQL ou pool do ORM |
| Queries ilimitadas | Sem LIMIT arrrisca retornar milhões de linhas | Sempre paginar |
| Armazenar dinheiro como FLOAT | Erros de arredondamento | Usar DECIMAL(19,4) ou centavos como inteiro |
| Tabelas deus | Uma tabela com 50+ colunas | Normalizar ou usar particionamento vertical |
| Soft deletes em todo lugar | Complica toda query com WHERE deleted_at IS NULL | Tabelas de arquivo ou event sourcing |
| Concatenação de string bruta | SQL injection | Sempre usar queries parametrizadas |
Referências Cruzadas
| Skill | Relacionamento |
|---|
| database-designer | Arquitetura de esquema, análise de normalização, geração ERD |
| database-schema-designer | Modelagem visual ERD, mapeamento de relacionamentos |
| migration-architect | Orquestração de migração complexa em múltiplos passos |
| api-design-reviewer | Garantir que endpoints de API se alinhem com padrões de query |
| observability-designer | Monitoramento de desempenho de queries, alertas de query lenta |