Database Workflows
by hgeldenhuys
Database workflows - schema design, migrations, query optimization. Use when designing schemas, reviewing migrations, optimizing queries, preventing N+1 problems, or working with ORMs like Prisma, Drizzle, and TypeORM.
Skill Details
Repository Files
4 files in this skill directory
name: database-workflows description: Database workflows - schema design, migrations, query optimization. Use when designing schemas, reviewing migrations, optimizing queries, preventing N+1 problems, or working with ORMs like Prisma, Drizzle, and TypeORM. version: 1.0.0 author: Claude Code SDK tags: [database, schema, migrations, sql]
Database Workflows
Quick reference for database work with Claude Code - schema design, migrations, query optimization, and ORM patterns.
Quick Reference
| Task | Key Action |
|---|---|
| Schema design | Normalize to 3NF, add indexes for queries |
| Migration review | Check reversibility, data preservation |
| Query optimization | Explain analyze, check indexes |
| N+1 prevention | Eager load relations, use joins |
| Index selection | Composite for multi-column WHERE |
When to Use This Skill
- Designing new database schemas
- Reviewing migration files before running
- Optimizing slow queries
- Debugging N+1 query problems
- Adding or reviewing indexes
- Working with Prisma, Drizzle, or TypeORM
Schema Design Checklist
Before creating or modifying schemas:
- Tables have singular names (
usernotusers) - Primary keys are
id(auto-increment or UUID) - Foreign keys follow
{table}_idpattern - Timestamps include
created_at,updated_at - Nullable columns are intentional
- Indexes cover common query patterns
- No redundant data (normalized to 3NF minimum)
See SCHEMA-DESIGN.md for detailed patterns.
Migration Workflow
Before Creating Migrations
# Prisma
bunx prisma migrate dev --create-only --name descriptive_name
# Drizzle
bunx drizzle-kit generate:pg --name descriptive_name
# TypeORM
bunx typeorm migration:generate -n DescriptiveName
Migration Review Checklist
- Migration is reversible (has down/rollback)
- No data loss on rollback
- Large tables use batched operations
- Indexes created CONCURRENTLY (if supported)
- Foreign key constraints don't lock tables
- Default values for new NOT NULL columns
See MIGRATIONS.md for strategies.
Query Optimization Quick Guide
Identify Slow Queries
-- PostgreSQL: Find slow queries
SELECT query, calls, mean_time, total_time
FROM pg_stat_statements
ORDER BY mean_time DESC
LIMIT 10;
-- MySQL: Enable slow query log
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1;
Analyze Queries
-- PostgreSQL
EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) SELECT ...;
-- MySQL
EXPLAIN ANALYZE SELECT ...;
Common Optimizations
| Problem | Solution |
|---|---|
| Full table scan | Add index on WHERE columns |
| Filesort | Add index matching ORDER BY |
| Using temporary | Optimize GROUP BY, add composite index |
| Seq Scan on large table | Add covering index |
See QUERIES.md for detailed optimization.
N+1 Query Prevention
Problem Pattern
// BAD: N+1 queries
const users = await db.user.findMany();
for (const user of users) {
const posts = await db.post.findMany({ where: { userId: user.id } });
}
Solution Pattern
// GOOD: Single query with relation
const users = await db.user.findMany({
include: { posts: true }
});
Detection
// Prisma: Enable query logging
const prisma = new PrismaClient({
log: ['query', 'info', 'warn', 'error'],
});
// Drizzle: Use query builder with joins
const result = await db
.select()
.from(users)
.leftJoin(posts, eq(users.id, posts.userId));
Index Quick Reference
When to Add Indexes
| Query Pattern | Index Type |
|---|---|
WHERE col = ? |
B-tree on col |
WHERE col1 = ? AND col2 = ? |
Composite (col1, col2) |
WHERE col LIKE 'prefix%' |
B-tree on col |
WHERE col @@ to_tsquery(?) |
GIN full-text |
ORDER BY col |
B-tree on col |
WHERE col IN (...) |
B-tree on col |
When NOT to Add Indexes
- Small tables (< 1000 rows)
- Columns with low cardinality
- Write-heavy tables with rare reads
- Columns rarely used in WHERE/ORDER BY
Index Commands
-- PostgreSQL: Create without locking
CREATE INDEX CONCURRENTLY idx_name ON table(column);
-- Check index usage
SELECT schemaname, tablename, indexname, idx_scan
FROM pg_stat_user_indexes
ORDER BY idx_scan ASC;
-- Find missing indexes
SELECT relname, seq_scan, idx_scan,
seq_scan - idx_scan AS difference
FROM pg_stat_user_tables
WHERE seq_scan > idx_scan
ORDER BY difference DESC;
ORM Patterns
Prisma
// Schema definition
model User {
id Int @id @default(autoincrement())
email String @unique
posts Post[]
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("users")
@@index([email])
}
// Efficient query with select
const users = await prisma.user.findMany({
select: { id: true, email: true },
where: { email: { contains: '@company.com' } },
take: 10,
});
Drizzle
// Schema definition
export const users = pgTable('users', {
id: serial('id').primaryKey(),
email: varchar('email', { length: 255 }).notNull().unique(),
createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow(),
}, (table) => ({
emailIdx: index('email_idx').on(table.email),
}));
// Efficient query with joins
const result = await db
.select({ id: users.id, email: users.email })
.from(users)
.where(like(users.email, '%@company.com'))
.limit(10);
TypeORM
// Entity definition
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ unique: true })
@Index()
email: string;
@CreateDateColumn({ name: 'created_at' })
createdAt: Date;
@UpdateDateColumn({ name: 'updated_at' })
updatedAt: Date;
@OneToMany(() => Post, post => post.user)
posts: Post[];
}
// Efficient query with QueryBuilder
const users = await userRepository
.createQueryBuilder('user')
.select(['user.id', 'user.email'])
.where('user.email LIKE :email', { email: '%@company.com' })
.take(10)
.getMany();
Database-Specific Patterns
PostgreSQL
-- UPSERT
INSERT INTO users (email, name)
VALUES ('test@example.com', 'Test')
ON CONFLICT (email)
DO UPDATE SET name = EXCLUDED.name, updated_at = NOW();
-- Array columns
ALTER TABLE users ADD COLUMN tags TEXT[];
CREATE INDEX idx_users_tags ON users USING GIN(tags);
SELECT * FROM users WHERE 'admin' = ANY(tags);
-- JSON columns
ALTER TABLE users ADD COLUMN metadata JSONB DEFAULT '{}';
CREATE INDEX idx_users_metadata ON users USING GIN(metadata);
SELECT * FROM users WHERE metadata->>'role' = 'admin';
MySQL
-- UPSERT
INSERT INTO users (email, name)
VALUES ('test@example.com', 'Test')
ON DUPLICATE KEY UPDATE name = VALUES(name), updated_at = NOW();
-- Full-text search
ALTER TABLE posts ADD FULLTEXT INDEX ft_content (title, content);
SELECT * FROM posts WHERE MATCH(title, content) AGAINST('search term');
SQLite
-- UPSERT
INSERT INTO users (email, name)
VALUES ('test@example.com', 'Test')
ON CONFLICT(email)
DO UPDATE SET name = excluded.name, updated_at = datetime('now');
-- Enable foreign keys (per connection)
PRAGMA foreign_keys = ON;
-- WAL mode for better concurrency
PRAGMA journal_mode = WAL;
Workflow: Schema Review
Prerequisites
- Schema file or migration to review
- Understanding of query patterns
Steps
-
Check Normalization
- No repeated groups
- All columns depend on primary key
- No transitive dependencies
-
Validate Relationships
- Foreign keys defined correctly
- Cascade rules appropriate
- Junction tables for many-to-many
-
Review Indexes
- Indexes on foreign keys
- Indexes on commonly queried columns
- Composite indexes in correct order
-
Check Constraints
- NOT NULL where required
- UNIQUE where appropriate
- CHECK constraints for valid ranges
Validation
- No N+1 patterns in expected queries
- Indexes support all common queries
- Schema can evolve without data loss
Workflow: Query Optimization
Prerequisites
- Slow query identified
- Access to EXPLAIN ANALYZE
Steps
-
Analyze Query Plan
- Run EXPLAIN ANALYZE
- Identify sequential scans
- Check join strategies
-
Identify Issues
- Missing indexes
- Incorrect join order
- Unnecessary columns in SELECT
-
Apply Fixes
- Add appropriate indexes
- Rewrite query if needed
- Use query hints if necessary
-
Verify Improvement
- Re-run EXPLAIN ANALYZE
- Compare execution times
- Test under load
Validation
- Query uses indexes effectively
- Execution time acceptable
- No regression in related queries
Common Mistakes
| Mistake | Fix |
|---|---|
| No index on foreign key | Add index on FK columns |
| SELECT * in production | Select only needed columns |
| N+1 in loops | Use eager loading or joins |
| Missing timestamps | Add created_at, updated_at |
| Nullable by default | Explicitly define NOT NULL |
| No migration rollback | Always write down migration |
Reference Files
| File | Contents |
|---|---|
| SCHEMA-DESIGN.md | Schema patterns, normalization, relationships |
| MIGRATIONS.md | Migration strategies, rollback, versioning |
| QUERIES.md | Query optimization, N+1 prevention, performance |
Related Skills
Xlsx
Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
Clickhouse Io
ClickHouse database patterns, query optimization, analytics, and data engineering best practices for high-performance analytical workloads.
Clickhouse Io
ClickHouse database patterns, query optimization, analytics, and data engineering best practices for high-performance analytical workloads.
Analyzing Financial Statements
This skill calculates key financial ratios and metrics from financial statement data for investment analysis
Data Storytelling
Transform data into compelling narratives using visualization, context, and persuasive structure. Use when presenting analytics to stakeholders, creating data reports, or building executive presentations.
Team Composition Analysis
This skill should be used when the user asks to "plan team structure", "determine hiring needs", "design org chart", "calculate compensation", "plan equity allocation", or requests organizational design and headcount planning for a startup.
Kpi Dashboard Design
Design effective KPI dashboards with metrics selection, visualization best practices, and real-time monitoring patterns. Use when building business dashboards, selecting metrics, or designing data visualization layouts.
Dbt Transformation Patterns
Master dbt (data build tool) for analytics engineering with model organization, testing, documentation, and incremental strategies. Use when building data transformations, creating data models, or implementing analytics engineering best practices.
Sql Optimization Patterns
Master SQL query optimization, indexing strategies, and EXPLAIN analysis to dramatically improve database performance and eliminate slow queries. Use when debugging slow queries, designing database schemas, or optimizing application performance.
Anndata
This skill should be used when working with annotated data matrices in Python, particularly for single-cell genomics analysis, managing experimental measurements with metadata, or handling large-scale biological datasets. Use when tasks involve AnnData objects, h5ad files, single-cell RNA-seq data, or integration with scanpy/scverse tools.
