workspace/projects/gamilit/docs/96-quick-reference/DB-CHEATSHEET.md
rckrdmrd 608e1e2a2e
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
Multi-project update: gamilit, orchestration, trading-platform
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>
2025-12-18 07:17:46 -06:00

17 KiB

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


🔌 Conexión Rápida

Desarrollo Local

# 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

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

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

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

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

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

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

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

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

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

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

-- 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.

-- 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.

-- Ya está aplicado automáticamente a todas las tablas principales
-- No necesitas llamarlo manualmente

Calcular Nivel desde XP

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

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

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

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

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

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

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

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

# 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

# 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

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

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

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

-- ❌ 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

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

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

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:

DDL Files:

PostgreSQL Docs:


Última actualización: 2025-12-18 Versión: 1.0 Método: Extracted from ESQUEMA-COMPLETO.md