option
MaisonMaison Skill Gestion de base de données sql-database-assistant

sql-database-assistant

alirezarezvani/claude-skills alirezarezvani/claude-skills

Traduisez le langage naturel en requêtes SQL, optimisez les performances des bases de données, générez des migrations, explorez les schémas et utilisez des ORM avec PostgreSQL, MySQL, SQLite et SQL Server.

...Développer tout
1
Heure mise à jour 2 septembre 2026

Assistant de base de données SQL - Compétence de niveau « POWERFUL »

Présentation

Le compagnon opérationnel de la conception de bases de données. Alors que le concepteur de bases de données se concentre sur l’architecture du schéma et que le concepteur de schémas de bases de données gère la modélisation ERD, cette compétence couvre les tâches quotidiennes : rédaction de requêtes, optimisation des performances, génération de migrations et mise en relation du code applicatif et des moteurs de bases de données.

Compétences clés

  • Du langage naturel au SQL — traduire les exigences en requêtes correctes et performantes
  • Exploration de schémas — analyse des bases de données en production sur PostgreSQL, MySQL, SQLite et SQL Server
  • Optimisation des requêtes — analyse EXPLAIN, recommandations d’indexation, détection des problèmes N+1, modèles de réécriture
  • Génération de migrations — scripts de mise à niveau et de retour en arrière, stratégies sans temps d’arrêt, plans de restauration
  • Intégration ORM — Prisma, Drizzle, TypeORM, SQLAlchemy : modèles et solutions de secours
  • Prise en charge multi-bases de données — SQL tenant compte des dialectes avec conseils de compatibilité

Outils

Script Objectif
scripts/query_optimizer.py Analyse statique des requêtes SQL pour détecter les problèmes de performances
scripts/migration_generator.py Génération de modèles de fichiers de migration à partir de descriptions de modifications
scripts/schema_explorer.py Génération de la documentation du schéma à partir de requêtes d'introspection

Du langage naturel vers le SQL

Modèles de traduction

Lors de la conversion des exigences en SQL, suivez cette séquence :

  1. Identifier les entités — associer les noms à des tables
  2. Identifier les relations — associer les verbes à des JOIN ou à des sous-requêtes
  3. Identifier les filtres — associer les adjectifs/conditions aux clauses WHERE
  4. Identifier les agrégations — associer « total », « moyenne », « nombre » à GROUP BY
  5. Identifier les critères de tri — associer « top », « latest », « highest » à ORDER BY + LIMIT

Modèles de requêtes courants

Top-N par groupe (fonction de fenêtre)

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

Totaux cumulés

SELECT date, montant,
  SUM(montant) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS total_cumulé
FROM transactions;

Détection des écarts

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

Consultez le fichier references/query_patterns.md pour en savoir plus sur les JOIN, les CTE, les fonctions de fenêtre, les opérations JSON et bien plus encore.

Exploration du schéma

Requêtes d’introspection

PostgreSQL — liste des tables et des colonnes

SELECT nom_table, nom_colonne, type_de_données, est_nullable, valeur_par_défaut
FROM information_schema.columns
WHERE schéma_table = 'public'
ORDER BY nom_table, position_ordonnée;

PostgreSQL — clés étrangères

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 — tailles des tables

SELECT nom_table, nombre_lignes,
  ROUND(longueur_données / 1024 / 1024, 2) AS données_mb,
  ROUND(longueur_index / 1024 / 1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
ORDER BY data_length DESC;

SQLite — sauvegarde du schéma

SELECT nom, sql FROM sqlite_master WHERE type = 'table' ORDER BY nom;

SQL Server — colonnes avec types

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;

Génération de la documentation à partir du schéma

Utilisez scripts/schema_explorer.py pour générer une documentation au format 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

Optimisation des requêtes

Workflow d’analyse EXPLAIN

  1. Exécutez EXPLAIN ANALYZE (PostgreSQL) ou EXPLAIN FORMAT=JSON (MySQL)
  2. Identifiez le nœud le plus coûteux — Seq Scan sur les grandes tables, Nested Loop avec des estimations de lignes élevées
  3. Vérifier s’il manque des index — balayages séquentiels sur des colonnes filtrées
  4. Recherchez les erreurs d’estimation — un écart entre le nombre de lignes prévu et réel indique que les statistiques sont obsolètes
  5. Évaluez l’ordre des JOIN — assurez-vous que le plus petit ensemble de résultats détermine la jointure

Liste de contrôle pour les recommandations d’index

  • Colonnes dans les clauses WHERE présentant une sélectivité élevée
  • Colonnes dans les conditions JOIN (clés étrangères)
  • Colonnes dans ORDER BY lorsqu’elles sont combinées avec LIMIT
  • Index composés correspondant à des prédicats WHERE sur plusieurs colonnes (en commençant par la colonne la plus sélective)
  • Index partiels pour les requêtes avec des filtres constants (par exemple, WHERE status = 'active')
  • Index couvrants pour éviter les recherches dans les tables pour les requêtes à forte intensité de lecture

Modèles de réécriture de requêtes

Anti-modèle Réécriture
SELECT * FROM commandes SELECT id, status, total FROM orders (colonnes explicites)
WHERE YEAR(created_at) = 2025 WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01' (sargable)
Sous-requête corrélée dans SELECT LEFT JOIN avec agrégation
NOT IN (SELECT ...) avec des valeurs NULL NOT EXISTS (SELECT 1 ...)
UNION (dedup) lorsque cela n'est pas nécessaire UNION ALL
LIKE '%search%' Index de recherche plein texte (GIN/FULLTEXT)
ORDER BY RAND() Échantillonnage aléatoire côté application ou TABLESAMPLE

Détection N+1

Symptômes :

  • Boucle d’application exécutant une requête par ligne parente
  • Chargement différé par l'ORM des entités associées à l'intérieur d'une boucle
  • Le journal des requêtes affiche des centaines de modèles SELECT identiques avec des ID différents

Solutions :

  • Utiliser le chargement anticipé (include dans Prisma, joinedload dans SQLAlchemy)
  • Regrouper les requêtes avec WHERE id IN (...)
  • Utiliser le modèle DataLoader pour les résolveurs GraphQL

Outil d'analyse statique

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

Consultez le fichier references/optimization_guide.md pour en savoir plus sur la lecture des plans EXPLAIN, les types d’index et la mise en pool des connexions.

Génération de migrations

Modèles de migration sans interruption de service

Ajout d’une colonne (sans risque)

-- Vers le haut
ALTER TABLE users ADD COLUMN phone VARCHAR(20);

-- Vers le bas
ALTER TABLE users DROP COLUMN phone;

Renommer une colonne (expansion-contraction)

-- Étape 1 : Ajouter une nouvelle colonne
ALTER TABLE users ADD COLUMN full_name VARCHAR(255);
-- Étape 2 : mise à jour rétrospective
UPDATE users SET full_name = name;
-- Étape 3 : déploiement de l'application lisant les deux colonnes
-- Étape 4 : déploiement de l'application écrivant uniquement dans la nouvelle colonne
-- Étape 5 : suppression de l'ancienne colonne
ALTER TABLE users DROP COLUMN name;

Ajout d’une colonne NOT NULL (séquence sécurisée)

-- Étape 1 : Ajouter une colonne pouvant contenir des valeurs NULL
ALTER TABLE orders ADD COLUMN region VARCHAR(50);
-- Étape 2 : Remplacement des anciennes valeurs par la valeur par défaut
UPDATE orders SET region = 'unknown' WHERE region IS NULL;
-- Étape 3 : Ajouter une contrainte
ALTER TABLE orders ALTER COLUMN region SET NOT NULL;
ALTER TABLE orders ALTER COLUMN region SET DEFAULT 'unknown';

Création d’un index (sans blocage, PostgreSQL)

CREATE INDEX CONCURRENTLY idx_orders_status ON orders (status);

Stratégies de remplissage des données

  • Mises à jour par lots — traitement par tranches de 1 000 à 10 000 lignes pour éviter les conflits de verrouillage
  • Tâches en arrière-plan — exécution asynchrone des réactualisations avec suivi de la progression
  • Double écriture — écriture simultanée dans les anciennes et les nouvelles colonnes pendant la période de transition
  • Requêtes de validation — vérification du nombre de lignes et de l’intégrité des données après chaque lot

Stratégies de restauration

Chaque migration doit disposer d’un script de restauration réversible. Pour les modifications irréversibles :

  1. Sauvegarde avant exécutionpg_dump des tables concernées
  2. Indicateurs de fonctionnalité — l’application peut basculer entre la lecture selon l’ancien et le nouveau schéma
  3. Tables miroirs — conserver une copie de la table d'origine pendant la fenêtre de migration

Outil de génération de migration

python scripts/migration_generator.py --change "ajouter le booléen email_verified à la table users" --dialect postgres --format sql
python scripts/migration_generator.py --change "renommer la colonne name en full_name dans la table customers" --dialect mysql --format alembic --json

Prise en charge de plusieurs bases de données

Différences entre dialectes

Fonctionnalité PostgreSQL MySQL SQLite SQL Server
UPSERT EN CAS DE CONFLIT, METTRE À JOUR EN CAS DE CLÉ DUPLIQUÉE, METTRE À JOUR EN CAS DE CONFLIT, METTRE À JOUR FUSION
Booléen BOOLEAN natif TINYINT(1) INTEGER BIT
Auto-incrément SÉRIE / GÉNÉRÉ AUTO_INCREMENT ENTIER CLÉ PRINCIPALE IDENTITY
JSON JSONB (indexé) JSON Texte (ext) NVARCHAR(MAX)
Tableau Tableau natif Non pris en charge Non pris en charge Non pris en charge
CTE (récursif) Prise en charge complète 8.0+ 3.8.3 Prise en charge complète
Fonctions de fenêtre Prise en charge complète 8.0+ 3.25.0+ Prise en charge complète
Recherche en texte intégral tsvector + GIN IndexFULLTEXT Extension FTS5 Catalogue en texte intégral
LIMIT/OFFSET LIMIT n OFFSET m LIMIT n OFFSET m LIMIT n OFFSET m OFFSET m LIGNES RÉCUPÉRER UNIQUEMENT LES n LIGNES SUIVANTES

Conseils de compatibilité

  • Utilisez toujours des requêtes paramétrées — cela empêche les injections SQL dans tous les dialectes
  • Évitez les fonctions spécifiques à un dialecte dans le code partagé — intégrez-les dans une couche d’adaptation
  • Testez les migrations sur le moteur cibleinformation_schema varie d’un moteur à l’autre
  • Utilisez le format de date ISO « AAAA-MM-JJ » fonctionne partout
  • Mettez les identifiants entre guillemets — utilisez des guillemets doubles (norme SQL) ou des guillemets inversés (MySQL)

Modèles ORM

Prisma

Définition du schéma

model User {
  id        Int      @id @default(autoincrement())
  email     String   @unique
  name      String?
  posts     Post[]
  createdAt DateTime @default(now())
}

modèle 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 API de requête: prisma.user.findMany({ where: { email: { contains: '@' } }, include: { posts: true } }) Porte de secours SQL brut: prisma.$queryRaw\SELECT * FROM users WHERE id = ${userId}``

Drizzle

Définition « schéma d'abord »

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

Générateur de requêtes: db.select().from(users).where(eq(users.email, email)) Migrations: npx drizzle-kit generate:pg puis npx drizzle-kit push:pg

TypeORM

Décorateurs d'entités

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

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

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

Modèle de référentiel: userRepo.find({ where: { email }, relations: ['posts'] }) Migrations: npx typeorm migration:generate -n AddUserEmail

SQLAlchemy

Modèles déclaratifs

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

Gestion des sessions: toujours utiliser with Session() en tant que gestionnaire de contexte Migrations Alembic: alembic revision --autogenerate -m "add user email"

Consultez le fichier references/orm_patterns.md pour des comparaisons côte à côte et les workflows de migration par ORM.

Intégrité des données

Stratégie de contraintes

  • Clés primaires — chaque table doit en posséder une ; privilégiez les clés de substitution (sérial/UUID)
  • Clés étrangères — garantissent l'intégrité référentielle ; définissez explicitement le comportement ON DELETE
  • Contraintes UNIQUE — pour garantir l’unicité au niveau métier (e-mail, slug, clé API)
  • Contraintes CHECK — valider les plages, les énumérations et les règles métier au niveau de la base de données
  • NOT NULL — par défaut, utiliser NOT NULL ; ne permettre les valeurs nulles que lorsque cela est véritablement facultatif

Niveaux d'isolation des transactions

Niveau Lecture « sale » Lecture non répétable Lecture fantôme Cas d’utilisation
LECTURE NON VALIDÉE Oui Oui Oui Jamais recommandé
LIRE AVEC ENGAGEMENT Non Oui Oui Par défaut pour PostgreSQL, OLTP général
LECTURE RÉPÉTABLE Non Non Oui (InnoDB : Non) Calculs financiers
SÉRIALISABLE Non Non Non Cohérence critique (facturation, stocks)

Prévention des interblocages

  1. Ordre cohérent des verrous — acquérir toujours les verrous dans le même ordre de tables/lignes
  2. Transactions courtes — réduire au minimum le délai entre le premier verrou et la validation
  3. Verrous consultatifs — utiliser pg_advisory_lock() pour la coordination au niveau de l’application
  4. Logique de réessai — détecter les erreurs d’interblocage et réessayer avec un recul exponentiel

Sauvegarde et restauration

PostgreSQL

# Sauvegarde complète
pg_dump -Fc --no-owner dbname > backup.dump
# Restauration
pg_restore -d dbname --clean --no-owner backup.dump
# Récupération à un instant donné : configurer l’archivage WAL + restore_command

MySQL

# Sauvegarde complète
mysqldump --single-transaction --routines --triggers nom_base_de_données > sauvegarde.sql
# Restauration
mysql dbname < backup.sql
# Journal binaire pour la restauration à un instant donné (PITR) : mysqlbinlog --start-datetime="2025-01-01 00:00:00" binlog.000001

SQLite

# Sauvegarde (sécurisée en cas de lectures simultanées)
sqlite3 nom_base_de_données ".backup backup.db"

Bonnes pratiques en matière de sauvegarde

  • Automatisation — cron ou minuteur systemd, jamais uniquement manuellement
  • Tester les restaurations — une sauvegarde non testée n'est pas une sauvegarde
  • Copies hors site — S3, GCS ou région distincte
  • Politique de conservation — quotidienne pendant 7 jours, hebdomadaire pendant 4 semaines, mensuelle pendant 12 mois
  • Surveillez la taille et la durée des sauvegardes — des changements soudains indiquent des problèmes

Anti-modèles

Anti-modèle Problème Solution
SELECT * Transfère des données inutiles, ne s'adapte pas aux modifications du schéma Liste explicite des colonnes
Indices manquants sur les colonnes FK JOIN lents et suppressions en cascade Ajouter des index sur toutes les clés étrangères
Requêtes N+1 1 + N allers-retours vers la base de données Chargement anticipé ou requêtes par lots
Coercition implicite de types WHERE id = '123' empêche l'utilisation de l'index Correspondance des types dans les prédicats
Pas de pool de connexions Épuisement des connexions en cas de charge élevée PgBouncer, ProxySQL ou pool ORM
Requêtes sans limite L'absence de LIMIT risque de renvoyer des millions de lignes Toujours paginer
Stockage des montants en FLOAT Erreurs d'arrondi Utilisez DECIMAL(19,4) ou des centimes entiers
Tables « God tables » Une table comportant plus de 50 colonnes Normaliser ou utiliser un partitionnement vertical
Suppressions temporaires partout Complique chaque requête avec WHERE deleted_at IS NULL Tables d’archivage ou « event sourcing »
Concaténation de chaînes brutes Injection SQL Requêtes paramétrées systématiquement

Références croisées

Compétence Relation
Concepteur de bases de données Architecture de schéma, analyse de normalisation, génération de diagrammes ER
concepteur-de-schémas-de-bases-de-données Modélisation visuelle d’ERD, cartographie des relations
architecte-de-migration Orchestration de migrations complexes en plusieurs étapes
vérificateur-de-conception-d'API Garantir la conformité des points de terminaison API avec les modèles de requêtes
plateforme-d'observabilité Surveillance des performances des requêtes, alertes en cas de requêtes lentes
Voir sur 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 |

Tous les fichiers

0 fichiers

Installer sql-database-assistant

Téléchargez et décompressez les fichiers de compétences dans votre répertoire .claude/skills/.

Télécharger le ZIP

Clonez le dépôt et copiez les fichiers de compétence dans votre projet.

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

Copier Copier
Configuration rapide: Copiez le dossier de la compétence dans .claude/skills/ Claude détectera automatiquement la compétence et l'utilisera

Compétences similaires

microservices-patterns
Heure mise à jour 29 juin 2026
jpa-patterns
Heure mise à jour 30 juin 2026
fabric-lakehouse
Heure mise à jour 30 juin 2026
prisma-expert
Heure mise à jour 29 juin 2026
OR