Use when designing database schemas and migrations.
Scanned 9/10/2026
Install to Claude Code
npx -y skills add LoopyLuci/Skills --skill database-design-patterns --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Database Design Patterns?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/loopyluci-database-design-patterns)More formats (shields.io, HTML) on the badges page.
---
name: database-design-patterns
description: "Use when designing database schemas and migrations."
version: 1.0.0
author: Hermes Agent
license: MIT
metadata:
hermes:
tags: [database, sql, nosql, schema, indexing, migration, postgresql]
related_skills: [sql-query-optimization, data-structures-algorithms, system-design-patterns, api-design-rest-graphql]
---
# Database Design Patterns
Designing efficient database schemas, indexing strategies, migration workflows, and data access patterns for SQL and NoSQL databases.
## When to Use
- Designing a new database schema from scratch
- Optimizing slow queries with proper indexing
- Planning database migrations without downtime
- Choosing between SQL, NoSQL, or hybrid approaches
- Modeling complex domain relationships in relational databases
## Schema Design Patterns
### Single Table Inheritance
```sql
CREATE TABLE assets (
id SERIAL PRIMARY KEY,
type VARCHAR(20) NOT NULL CHECK (type IN ('document', 'image', 'video')),
title TEXT NOT NULL,
page_count INTEGER,
width INTEGER, height INTEGER,
duration INTEGER, resolution VARCHAR(20),
created_at TIMESTAMPTZ DEFAULT NOW()
);
```
### Concrete Table Inheritance
```sql
CREATE TABLE documents (
id SERIAL PRIMARY KEY, title TEXT NOT NULL, page_count INTEGER
);
CREATE TABLE images (
id SERIAL PRIMARY KEY, title TEXT NOT NULL, width INTEGER, height INTEGER
);
```
### Class Table Inheritance
```sql
CREATE TABLE assets (
id SERIAL PRIMARY KEY, type VARCHAR(20) NOT NULL, title TEXT NOT NULL
);
CREATE TABLE documents (asset_id INTEGER PRIMARY KEY REFERENCES assets(id), page_count INTEGER);
CREATE TABLE images (asset_id INTEGER PRIMARY KEY REFERENCES assets(id), width INTEGER, height INTEGER);
```
## Indexing Patterns
### Composite Index Column Order
```sql
-- For: WHERE status = 'active' AND created_at > '2024-01-01'
-- Place high-selectivity columns FIRST
CREATE INDEX idx_orders_status_created ON orders (status, created_at);
```
### Covering Index
```sql
-- Index-Only Scan: never touches the heap
-- For: SELECT user_id, email FROM users WHERE status = 'active';
CREATE INDEX idx_users_status_covering ON users (status) INCLUDE (user_id, email);
```
### Partial Index
```sql
-- Index only active records (much smaller, faster writes)
CREATE INDEX idx_orders_active ON orders (created_at) WHERE status = 'active';
```
### Expression Index
```sql
CREATE INDEX idx_users_lower_email ON users (LOWER(email));
-- SELECT * FROM users WHERE LOWER(email) = 'user@example.com';
```
## Migration Patterns
### Expand-Contract (Zero-Downtime)
```sql
-- Phase 1: EXPAND — add new column
ALTER TABLE users ADD COLUMN email_new VARCHAR(255);
-- App writes to both columns
-- Phase 2: Backfill
UPDATE users SET email_new = email WHERE email_new IS NULL;
-- Phase 3: CONTRACT
ALTER TABLE users DROP COLUMN email;
ALTER TABLE users RENAME COLUMN email_new TO email;
```
## NoSQL Patterns
### Document Denormalization
```python
user = {
"_id": "user_123",
"name": "Alice",
"addresses": [
{"type": "home", "street": "123 Main St"},
{"type": "work", "street": "456 Corp Ave"}
]
}
# Pro: Single read for user + addresses
# Con: Update anomalies on separately-edited embedded docs
```
### CQRS
```python
class CQRSHandler:
def __init__(self):
self.write_db = PostgresWriteDB() # Normalized
self.read_db = ElasticsearchReadDB() # Denormalized
def handle_command(self, command):
result = self.write_db.execute(command)
self.sync_to_read_model(result)
return result
def handle_query(self, query):
return self.read_db.search(query)
```
## Common Pitfalls
1. **Premature denormalization** — normalize first, denormalize only when query patterns demand it
2. **Missing foreign keys** — relational integrity enforced at app level drifts over time
3. **Index-everything** — writes become slow; index only what queries filter on
4. **SELECT * in production** — always name columns; breaks when schema changes
5. **No migration plan** — schema changes without backfill cause downtime
6. **Ignoring EXPLAIN ANALYZE** — the optimizer shows what's really happening; always check
## Verification Checklist
- [ ] Schema normalized to 3NF before any denormalization
- [ ] Indexes match actual query patterns (check slow query log)
- [ ] Migrations are reversible (up + down functions)
- [ ] Foreign keys enforced at DB level
- [ ] EXPLAIN ANALYZE confirms index usage on critical queries
- [ ] No N+1 queries in common access patterns
## See Also
- sql-query-optimization — optimizing queries against your schema
- data-structures-algorithms — choosing right data structures
- system-design-patterns — database at scale
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!