Some checks are pending
CI Pipeline / changes (push) Waiting to run
CI Pipeline / core (push) Blocked by required conditions
CI Pipeline / trading-backend (push) Blocked by required conditions
CI Pipeline / trading-data-service (push) Blocked by required conditions
CI Pipeline / trading-frontend (push) Blocked by required conditions
CI Pipeline / erp-core (push) Blocked by required conditions
CI Pipeline / erp-mecanicas (push) Blocked by required conditions
CI Pipeline / gamilit-backend (push) Blocked by required conditions
CI Pipeline / gamilit-frontend (push) Blocked by required conditions
Gamilit: - Backend: Teacher services, assignments, gamification, exercise submissions - Frontend: Admin/Teacher/Student portals, module 4-5 mechanics, monitoring - Database: DDL functions, seeds for dev/prod, auth/gamification schemas - Docs: Architecture, features, guides cleanup and reorganization Core/Orchestration: - New workspace directives index - Documentation directive Trading-platform: - Database seeds and inventory updates - Tech leader validation report 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
716 lines
17 KiB
Markdown
716 lines
17 KiB
Markdown
# Database Cheatsheet - GAMILIT Platform
|
|
|
|
**Versión:** 1.0
|
|
**Última actualización:** 2025-12-18
|
|
**Database:** PostgreSQL 15+
|
|
**Timezone:** America/Mexico_City
|
|
|
|
> **Nota:** Este es un cheatsheet rápido. Para esquema completo ver [docs/03-desarrollo/base-de-datos/ESQUEMA-COMPLETO.md](../03-desarrollo/base-de-datos/ESQUEMA-COMPLETO.md)
|
|
|
|
---
|
|
|
|
## 🔌 Conexión Rápida
|
|
|
|
### Desarrollo Local
|
|
|
|
```bash
|
|
# Conectar a database
|
|
psql -U gamilit_user -d gamilit_platform -h localhost
|
|
|
|
# Conectar con password inline (no recomendado en producción)
|
|
PGPASSWORD=gamilit_dev_2025 psql -U gamilit_user -d gamilit_platform -h localhost
|
|
```
|
|
|
|
### Variables de Entorno
|
|
|
|
```bash
|
|
export PGHOST=localhost
|
|
export PGPORT=5432
|
|
export PGDATABASE=gamilit_platform
|
|
export PGUSER=gamilit_user
|
|
export PGPASSWORD=gamilit_dev_2025
|
|
|
|
# Ahora solo:
|
|
psql
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Esquema General
|
|
|
|
### Resumen
|
|
|
|
- **Schemas:** 16
|
|
- **Tablas:** 123
|
|
- **Funciones:** 213
|
|
- **Triggers:** 90
|
|
- **Índices:** 279+
|
|
- **RLS Policies:** 185
|
|
|
|
### Schemas Principales
|
|
|
|
| Schema | Tablas | Propósito |
|
|
|--------|--------|-----------|
|
|
| **auth_management** | 12 | Autenticación, usuarios, sesiones, roles |
|
|
| **educational_content** | 8 | Módulos, ejercicios, contenido educativo |
|
|
| **gamification_system** | 10 | ML Coins, rangos, achievements, power-ups |
|
|
| **progress_tracking** | 6 | Progreso, intentos, sesiones de aprendizaje |
|
|
| **social_features** | 7 | Escuelas, aulas, equipos, amistades |
|
|
| **content_management** | 4 | Media files, Marie Curie content |
|
|
| **audit_logging** | 2 | Logs de auditoría, eventos de seguridad |
|
|
| **system_configuration** | 1 | Configuración del sistema |
|
|
| **public** | 2 | Schema público |
|
|
|
|
---
|
|
|
|
## 🗂️ Comandos Útiles de psql
|
|
|
|
### Navegación
|
|
|
|
```sql
|
|
-- Listar schemas
|
|
\dn
|
|
|
|
-- Listar tablas de un schema
|
|
\dt auth_management.*
|
|
|
|
-- Listar todas las tablas
|
|
\dt *.*
|
|
|
|
-- Describir tabla
|
|
\d auth_management.profiles
|
|
|
|
-- Listar funciones
|
|
\df
|
|
|
|
-- Listar triggers de una tabla
|
|
\dy auth_management.profiles
|
|
|
|
-- Listar índices de una tabla
|
|
\di auth_management.profiles
|
|
|
|
-- Ver tamaño de tablas
|
|
\dt+ *.*
|
|
|
|
-- Ayuda
|
|
\?
|
|
|
|
-- Salir
|
|
\q
|
|
```
|
|
|
|
### Configuración
|
|
|
|
```sql
|
|
-- Mostrar configuración actual
|
|
\set
|
|
|
|
-- Timing de queries
|
|
\timing on
|
|
|
|
-- Formato expandido (mejor para filas anchas)
|
|
\x auto
|
|
|
|
-- Paginación
|
|
\pset pager off
|
|
```
|
|
|
|
---
|
|
|
|
## 🔍 Queries Comunes por Caso de Uso
|
|
|
|
### 1. Autenticación
|
|
|
|
```sql
|
|
-- Buscar usuario por email
|
|
SELECT id, email, full_name, role, status
|
|
FROM auth_management.profiles
|
|
WHERE email = 'student@example.com';
|
|
|
|
-- Verificar sesiones activas de un usuario
|
|
SELECT session_token, device_type, ip_address, expires_at, created_at
|
|
FROM auth_management.user_sessions
|
|
WHERE user_id = 'user-uuid'
|
|
AND expires_at > NOW()
|
|
ORDER BY created_at DESC;
|
|
|
|
-- Contar intentos de login fallidos recientes
|
|
SELECT COUNT(*)
|
|
FROM auth_management.auth_attempts
|
|
WHERE user_id = 'user-uuid'
|
|
AND success = false
|
|
AND created_at > NOW() - INTERVAL '15 minutes';
|
|
|
|
-- Listar usuarios por rol
|
|
SELECT id, email, full_name, role, status, created_at
|
|
FROM auth_management.profiles
|
|
WHERE role = 'student'
|
|
AND status = 'active'
|
|
ORDER BY created_at DESC
|
|
LIMIT 10;
|
|
```
|
|
|
|
### 2. Gamificación
|
|
|
|
```sql
|
|
-- Stats generales de un usuario
|
|
SELECT user_id, level, total_xp, ml_coins,
|
|
current_streak, max_streak,
|
|
exercises_completed, average_score,
|
|
global_rank_position
|
|
FROM gamification_system.user_stats
|
|
WHERE user_id = 'user-uuid';
|
|
|
|
-- Balance de ML Coins actual
|
|
SELECT ml_coins
|
|
FROM gamification_system.user_stats
|
|
WHERE user_id = 'user-uuid';
|
|
|
|
-- Historial de transacciones ML Coins (últimas 20)
|
|
SELECT id, amount, transaction_type, reason,
|
|
balance_after, created_at
|
|
FROM gamification_system.ml_coins_ledger
|
|
WHERE user_id = 'user-uuid'
|
|
ORDER BY created_at DESC
|
|
LIMIT 20;
|
|
|
|
-- Top 10 ranking global
|
|
SELECT u.user_id, p.display_name, u.total_xp,
|
|
u.level, u.ml_coins, u.global_rank_position
|
|
FROM gamification_system.user_stats u
|
|
JOIN auth_management.profiles p ON u.user_id = p.id
|
|
WHERE u.global_rank_position IS NOT NULL
|
|
ORDER BY u.global_rank_position ASC
|
|
LIMIT 10;
|
|
|
|
-- Achievements desbloqueados por usuario
|
|
SELECT ua.achievement_id, a.name, a.description,
|
|
a.category, a.rarity, a.ml_coins_reward,
|
|
ua.unlocked_at
|
|
FROM gamification_system.user_achievements ua
|
|
JOIN gamification_system.achievements a ON ua.achievement_id = a.id
|
|
WHERE ua.user_id = 'user-uuid'
|
|
AND ua.is_claimed = true
|
|
ORDER BY ua.unlocked_at DESC;
|
|
|
|
-- Rango actual del usuario (Maya ranks)
|
|
SELECT r.rank_name, r.min_xp_required, r.benefits
|
|
FROM gamification_system.user_ranks ur
|
|
JOIN gamification_system.ranks r ON ur.rank_id = r.id
|
|
WHERE ur.user_id = 'user-uuid'
|
|
AND ur.is_current = true;
|
|
```
|
|
|
|
### 3. Progreso Educativo
|
|
|
|
```sql
|
|
-- Progreso general del estudiante
|
|
SELECT m.title AS module_name,
|
|
mp.status,
|
|
mp.progress_percentage,
|
|
mp.completed_exercises,
|
|
mp.total_exercises,
|
|
mp.average_score,
|
|
mp.time_spent,
|
|
mp.last_accessed_at
|
|
FROM progress_tracking.module_progress mp
|
|
JOIN educational_content.modules m ON mp.module_id = m.id
|
|
WHERE mp.user_id = 'user-uuid'
|
|
ORDER BY mp.last_accessed_at DESC;
|
|
|
|
-- Ejercicios completados recientemente
|
|
SELECT e.title AS exercise_name,
|
|
ea.score,
|
|
ea.is_correct,
|
|
ea.time_spent,
|
|
ea.attempt_number,
|
|
ea.created_at
|
|
FROM progress_tracking.exercise_attempts ea
|
|
JOIN educational_content.exercises e ON ea.exercise_id = e.id
|
|
WHERE ea.user_id = 'user-uuid'
|
|
AND ea.created_at > NOW() - INTERVAL '7 days'
|
|
ORDER BY ea.created_at DESC
|
|
LIMIT 20;
|
|
|
|
-- Estadísticas de un módulo específico
|
|
SELECT status,
|
|
completed_exercises || '/' || total_exercises AS progress,
|
|
progress_percentage || '%' AS percentage,
|
|
ROUND(average_score, 2) AS avg_score,
|
|
time_spent,
|
|
last_accessed_at
|
|
FROM progress_tracking.module_progress
|
|
WHERE user_id = 'user-uuid'
|
|
AND module_id = 'module-uuid';
|
|
|
|
-- Mecánicas más completadas
|
|
SELECT e.mechanic_type, COUNT(*) AS times_completed,
|
|
ROUND(AVG(ea.score), 2) AS avg_score
|
|
FROM progress_tracking.exercise_attempts ea
|
|
JOIN educational_content.exercises e ON ea.exercise_id = e.id
|
|
WHERE ea.user_id = 'user-uuid'
|
|
AND ea.is_correct = true
|
|
GROUP BY e.mechanic_type
|
|
ORDER BY times_completed DESC;
|
|
```
|
|
|
|
### 4. Teacher Dashboard
|
|
|
|
```sql
|
|
-- Aulas del profesor
|
|
SELECT c.id, c.name, c.grade_level,
|
|
c.current_students_count, c.capacity,
|
|
s.name AS school_name
|
|
FROM social_features.classrooms c
|
|
LEFT JOIN social_features.schools s ON c.school_id = s.id
|
|
WHERE c.teacher_id = 'teacher-uuid'
|
|
AND c.is_active = true
|
|
ORDER BY c.created_at DESC;
|
|
|
|
-- Estudiantes de un aula
|
|
SELECT p.id, p.display_name, p.email,
|
|
us.level, us.total_xp, us.ml_coins,
|
|
cm.enrolled_at
|
|
FROM social_features.classroom_members cm
|
|
JOIN auth_management.profiles p ON cm.student_id = p.id
|
|
JOIN gamification_system.user_stats us ON p.id = us.user_id
|
|
WHERE cm.classroom_id = 'classroom-uuid'
|
|
AND cm.status = 'active'
|
|
ORDER BY us.total_xp DESC;
|
|
|
|
-- Progreso de estudiantes en un módulo
|
|
SELECT p.display_name,
|
|
mp.status,
|
|
mp.progress_percentage,
|
|
mp.average_score,
|
|
mp.time_spent
|
|
FROM progress_tracking.module_progress mp
|
|
JOIN auth_management.profiles p ON mp.user_id = p.id
|
|
JOIN social_features.classroom_members cm
|
|
ON cm.student_id = p.id
|
|
WHERE cm.classroom_id = 'classroom-uuid'
|
|
AND mp.module_id = 'module-uuid'
|
|
ORDER BY mp.progress_percentage DESC, mp.average_score DESC;
|
|
|
|
-- Estudiantes que necesitan atención (bajo rendimiento)
|
|
SELECT p.display_name, p.email,
|
|
us.exercises_completed,
|
|
us.average_score,
|
|
us.current_streak,
|
|
us.last_active_at
|
|
FROM gamification_system.user_stats us
|
|
JOIN auth_management.profiles p ON us.user_id = p.id
|
|
JOIN social_features.classroom_members cm ON p.id = cm.student_id
|
|
WHERE cm.classroom_id = 'classroom-uuid'
|
|
AND (us.average_score < 70 OR us.current_streak = 0)
|
|
ORDER BY us.average_score ASC;
|
|
```
|
|
|
|
### 5. Analytics
|
|
|
|
```sql
|
|
-- Actividad diaria de la plataforma (últimos 7 días)
|
|
SELECT DATE(created_at) AS date,
|
|
COUNT(DISTINCT user_id) AS active_users,
|
|
COUNT(*) AS total_exercises
|
|
FROM progress_tracking.exercise_attempts
|
|
WHERE created_at > NOW() - INTERVAL '7 days'
|
|
GROUP BY DATE(created_at)
|
|
ORDER BY date DESC;
|
|
|
|
-- Sesiones de aprendizaje promedio por usuario
|
|
SELECT u.user_id, p.display_name,
|
|
COUNT(ls.id) AS total_sessions,
|
|
AVG(EXTRACT(EPOCH FROM ls.duration) / 60) AS avg_duration_minutes,
|
|
SUM(ls.exercises_completed) AS total_exercises
|
|
FROM progress_tracking.learning_sessions ls
|
|
JOIN gamification_system.user_stats u ON ls.user_id = u.user_id
|
|
JOIN auth_management.profiles p ON u.user_id = p.id
|
|
WHERE ls.created_at > NOW() - INTERVAL '30 days'
|
|
GROUP BY u.user_id, p.display_name
|
|
HAVING COUNT(ls.id) > 5
|
|
ORDER BY total_sessions DESC
|
|
LIMIT 20;
|
|
|
|
-- Retención de usuarios (días activos)
|
|
SELECT days_active_total AS days_active,
|
|
COUNT(*) AS user_count
|
|
FROM gamification_system.user_stats
|
|
GROUP BY days_active_total
|
|
ORDER BY days_active_total DESC;
|
|
|
|
-- Conversión de intentos a completaciones
|
|
SELECT e.title,
|
|
COUNT(*) AS total_attempts,
|
|
SUM(CASE WHEN ea.is_correct THEN 1 ELSE 0 END) AS successful,
|
|
ROUND(100.0 * SUM(CASE WHEN ea.is_correct THEN 1 ELSE 0 END) / COUNT(*), 2) AS success_rate
|
|
FROM progress_tracking.exercise_attempts ea
|
|
JOIN educational_content.exercises e ON ea.exercise_id = e.id
|
|
WHERE ea.created_at > NOW() - INTERVAL '30 days'
|
|
GROUP BY e.id, e.title
|
|
HAVING COUNT(*) >= 10
|
|
ORDER BY success_rate ASC
|
|
LIMIT 10;
|
|
```
|
|
|
|
---
|
|
|
|
## 🔗 Relaciones Principales
|
|
|
|
### User → Stats → Progress
|
|
|
|
```sql
|
|
-- Diagrama de relaciones
|
|
auth_management.profiles (id)
|
|
↓
|
|
gamification_system.user_stats (user_id)
|
|
↓
|
|
progress_tracking.module_progress (user_id)
|
|
↓
|
|
progress_tracking.exercise_attempts (user_id)
|
|
```
|
|
|
|
### School → Classroom → Students
|
|
|
|
```sql
|
|
-- Diagrama de relaciones
|
|
social_features.schools (id)
|
|
↓
|
|
social_features.classrooms (school_id, teacher_id)
|
|
↓
|
|
social_features.classroom_members (classroom_id, student_id)
|
|
↓
|
|
auth_management.profiles (id)
|
|
```
|
|
|
|
### Module → Exercise → Attempts
|
|
|
|
```sql
|
|
-- Diagrama de relaciones
|
|
educational_content.modules (id)
|
|
↓
|
|
educational_content.exercises (module_id)
|
|
↓
|
|
progress_tracking.exercise_attempts (exercise_id, user_id)
|
|
```
|
|
|
|
---
|
|
|
|
## ⚡ Funciones Útiles
|
|
|
|
### Función: `gamilit.now_mexico()`
|
|
|
|
Retorna timestamp en zona horaria de México.
|
|
|
|
```sql
|
|
-- Uso
|
|
SELECT gamilit.now_mexico();
|
|
|
|
-- Comparar con UTC
|
|
SELECT NOW() AS utc_time, gamilit.now_mexico() AS mexico_time;
|
|
```
|
|
|
|
### Función: `gamilit.update_updated_at()`
|
|
|
|
Trigger function para actualizar automáticamente `updated_at`.
|
|
|
|
```sql
|
|
-- Ya está aplicado automáticamente a todas las tablas principales
|
|
-- No necesitas llamarlo manualmente
|
|
```
|
|
|
|
### Calcular Nivel desde XP
|
|
|
|
```sql
|
|
-- Fórmula: level = floor(sqrt(total_xp / 100)) + 1
|
|
SELECT FLOOR(SQRT(total_xp / 100)) + 1 AS calculated_level
|
|
FROM gamification_system.user_stats
|
|
WHERE user_id = 'user-uuid';
|
|
```
|
|
|
|
---
|
|
|
|
## 📈 Índices y Performance
|
|
|
|
### Ver índices de una tabla
|
|
|
|
```sql
|
|
-- Listar índices
|
|
SELECT indexname, indexdef
|
|
FROM pg_indexes
|
|
WHERE tablename = 'profiles'
|
|
AND schemaname = 'auth_management';
|
|
|
|
-- Uso de índices (requiere pg_stat_statements)
|
|
SELECT schemaname, tablename, indexname,
|
|
idx_scan, idx_tup_read, idx_tup_fetch
|
|
FROM pg_stat_user_indexes
|
|
WHERE schemaname = 'auth_management'
|
|
ORDER BY idx_scan DESC;
|
|
```
|
|
|
|
### Índices más importantes
|
|
|
|
```sql
|
|
-- Email lookup (muy frecuente)
|
|
CREATE INDEX idx_profiles_email ON auth_management.profiles(email);
|
|
|
|
-- User stats lookup
|
|
CREATE INDEX idx_user_stats_user_id ON gamification_system.user_stats(user_id);
|
|
|
|
-- Exercise attempts por usuario
|
|
CREATE INDEX idx_exercise_attempts_user_id
|
|
ON progress_tracking.exercise_attempts(user_id);
|
|
|
|
-- Sesiones activas
|
|
CREATE INDEX idx_sessions_expires
|
|
ON auth_management.user_sessions(expires_at)
|
|
WHERE expires_at > NOW();
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 RLS (Row Level Security)
|
|
|
|
### Verificar políticas RLS
|
|
|
|
```sql
|
|
-- Listar políticas de una tabla
|
|
SELECT schemaname, tablename, policyname, cmd, qual
|
|
FROM pg_policies
|
|
WHERE tablename = 'profiles';
|
|
|
|
-- Verificar si RLS está habilitado
|
|
SELECT schemaname, tablename, rowsecurity
|
|
FROM pg_tables
|
|
WHERE schemaname = 'auth_management';
|
|
```
|
|
|
|
### Políticas comunes
|
|
|
|
```sql
|
|
-- Students can only see their own data
|
|
CREATE POLICY students_view_own_data
|
|
ON progress_tracking.module_progress
|
|
FOR SELECT
|
|
USING (user_id = current_setting('app.current_user_id')::UUID);
|
|
|
|
-- Teachers can see their classroom students
|
|
CREATE POLICY teachers_view_classroom_data
|
|
ON progress_tracking.module_progress
|
|
FOR SELECT
|
|
USING (
|
|
EXISTS (
|
|
SELECT 1
|
|
FROM social_features.classroom_members cm
|
|
JOIN social_features.classrooms c ON cm.classroom_id = c.id
|
|
WHERE cm.student_id = module_progress.user_id
|
|
AND c.teacher_id = current_setting('app.current_user_id')::UUID
|
|
)
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 🛠️ Mantenimiento
|
|
|
|
### Vacuum y Analyze
|
|
|
|
```sql
|
|
-- Vacuum de una tabla
|
|
VACUUM ANALYZE auth_management.profiles;
|
|
|
|
-- Vacuum de todo el schema
|
|
VACUUM ANALYZE auth_management.*;
|
|
|
|
-- Ver última vez que se ejecutó
|
|
SELECT schemaname, relname, last_vacuum, last_autovacuum, last_analyze
|
|
FROM pg_stat_user_tables
|
|
WHERE schemaname = 'auth_management'
|
|
ORDER BY last_vacuum DESC NULLS LAST;
|
|
```
|
|
|
|
### Ver tamaño de tablas
|
|
|
|
```sql
|
|
-- Tamaño de una tabla
|
|
SELECT pg_size_pretty(pg_total_relation_size('auth_management.profiles'));
|
|
|
|
-- Top 10 tablas más grandes
|
|
SELECT schemaname, tablename,
|
|
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
|
|
FROM pg_tables
|
|
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
|
|
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
|
|
LIMIT 10;
|
|
```
|
|
|
|
### Ver locks activos
|
|
|
|
```sql
|
|
-- Locks actuales
|
|
SELECT pid, usename, pg_blocking_pids(pid) AS blocked_by,
|
|
query, state
|
|
FROM pg_stat_activity
|
|
WHERE pg_blocking_pids(pid)::text != '{}';
|
|
|
|
-- Terminar query lento
|
|
SELECT pg_terminate_backend(pid)
|
|
FROM pg_stat_activity
|
|
WHERE pid = 12345;
|
|
```
|
|
|
|
---
|
|
|
|
## 📊 Backups
|
|
|
|
### Backup completo
|
|
|
|
```bash
|
|
# Backup de toda la database
|
|
pg_dump -U gamilit_user -d gamilit_platform -h localhost \
|
|
-F c -f gamilit_backup_$(date +%Y%m%d).dump
|
|
|
|
# Backup de un schema específico
|
|
pg_dump -U gamilit_user -d gamilit_platform -h localhost \
|
|
-n auth_management -F c -f auth_schema_$(date +%Y%m%d).dump
|
|
|
|
# Backup en SQL text
|
|
pg_dump -U gamilit_user -d gamilit_platform -h localhost \
|
|
-F p -f gamilit_backup_$(date +%Y%m%d).sql
|
|
```
|
|
|
|
### Restore
|
|
|
|
```bash
|
|
# Restore desde custom format
|
|
pg_restore -U gamilit_user -d gamilit_platform -h localhost \
|
|
-c gamilit_backup_20251107.dump
|
|
|
|
# Restore desde SQL text
|
|
psql -U gamilit_user -d gamilit_platform -h localhost \
|
|
-f gamilit_backup_20251107.sql
|
|
```
|
|
|
|
---
|
|
|
|
## 🧪 Testing Queries
|
|
|
|
### Crear usuario de prueba
|
|
|
|
```sql
|
|
-- Insertar perfil
|
|
INSERT INTO auth_management.profiles
|
|
(id, email, full_name, display_name, role, status)
|
|
VALUES
|
|
(gen_random_uuid(), 'test@example.com', 'Test User', 'Test', 'student', 'active')
|
|
RETURNING id;
|
|
|
|
-- Stats se crean automáticamente por trigger
|
|
```
|
|
|
|
### Limpiar datos de prueba
|
|
|
|
```sql
|
|
-- Eliminar usuario de prueba (cascade eliminará stats, progress, etc.)
|
|
DELETE FROM auth_management.profiles
|
|
WHERE email LIKE '%@example.com';
|
|
|
|
-- Resetear sequence (si aplica)
|
|
ALTER SEQUENCE some_sequence RESTART WITH 1;
|
|
```
|
|
|
|
### Ver queries lentas (últimas 24h)
|
|
|
|
```sql
|
|
-- Requiere pg_stat_statements extension
|
|
SELECT calls, total_exec_time, mean_exec_time,
|
|
substring(query, 1, 100) AS query_preview
|
|
FROM pg_stat_statements
|
|
WHERE total_exec_time > 1000 -- más de 1 segundo total
|
|
ORDER BY mean_exec_time DESC
|
|
LIMIT 10;
|
|
```
|
|
|
|
---
|
|
|
|
## 💡 Tips y Best Practices
|
|
|
|
### 1. Siempre usar tenant_id en queries multi-tenant
|
|
|
|
```sql
|
|
-- ❌ MAL (no filtra por tenant)
|
|
SELECT * FROM auth_management.profiles WHERE email = 'test@example.com';
|
|
|
|
-- ✅ BIEN (filtra por tenant)
|
|
SELECT * FROM auth_management.profiles
|
|
WHERE email = 'test@example.com'
|
|
AND tenant_id = 'tenant-uuid';
|
|
```
|
|
|
|
### 2. Usar prepared statements
|
|
|
|
```sql
|
|
-- Preparar statement
|
|
PREPARE get_user_stats (UUID) AS
|
|
SELECT level, total_xp, ml_coins, current_streak
|
|
FROM gamification_system.user_stats
|
|
WHERE user_id = $1;
|
|
|
|
-- Ejecutar
|
|
EXECUTE get_user_stats('user-uuid');
|
|
|
|
-- Deallocate cuando termines
|
|
DEALLOCATE get_user_stats;
|
|
```
|
|
|
|
### 3. EXPLAIN para entender queries lentas
|
|
|
|
```sql
|
|
-- Ver plan de ejecución
|
|
EXPLAIN SELECT * FROM auth_management.profiles WHERE email = 'test@example.com';
|
|
|
|
-- Ver plan con costos reales
|
|
EXPLAIN ANALYZE SELECT * FROM auth_management.profiles WHERE email = 'test@example.com';
|
|
```
|
|
|
|
### 4. Transacciones para operaciones atómicas
|
|
|
|
```sql
|
|
BEGIN;
|
|
-- Restar ML Coins
|
|
UPDATE gamification_system.user_stats
|
|
SET ml_coins = ml_coins - 50
|
|
WHERE user_id = 'user-uuid'
|
|
AND ml_coins >= 50;
|
|
|
|
-- Registrar transacción
|
|
INSERT INTO gamification_system.ml_coins_ledger
|
|
(user_id, amount, transaction_type, reason, balance_after)
|
|
VALUES
|
|
('user-uuid', -50, 'powerup_purchase', 'Bought hint', 450);
|
|
COMMIT;
|
|
```
|
|
|
|
---
|
|
|
|
## 📚 Recursos
|
|
|
|
**Documentación completa:**
|
|
- [ESQUEMA-COMPLETO.md](../03-desarrollo/base-de-datos/ESQUEMA-COMPLETO.md) - Esquema detallado de 44 tablas
|
|
- [RLS-POLICIES.md](../03-desarrollo/base-de-datos/RLS-POLICIES.md) - Políticas de Row Level Security
|
|
- [MIGRACIONES.md](../03-desarrollo/base-de-datos/MIGRACIONES.md) - Sistema de migraciones
|
|
|
|
**DDL Files:**
|
|
- [apps/database/ddl/](../../apps/database/ddl/) - Definiciones de esquema
|
|
|
|
**PostgreSQL Docs:**
|
|
- [PostgreSQL 15 Documentation](https://www.postgresql.org/docs/15/)
|
|
- [psql Commands](https://www.postgresql.org/docs/15/app-psql.html)
|
|
|
|
---
|
|
|
|
**Última actualización:** 2025-12-18
|
|
**Versión:** 1.0
|
|
**Método:** Extracted from ESQUEMA-COMPLETO.md
|