The RuedaPro UNIPAZ database uses a normalized relational schema with the proyectos table as the central entity. Two junction tables (proyecto_evaluadores and proyecto_estudiantes) implement many-to-many relationships, while the evaluaciones table creates a one-to-many relationship between evaluators and projects.
Junction Table:proyecto_evaluadoresA project can be evaluated by multiple docentes (typically 1-3), and each docente can evaluate multiple projects. The junction table enforces that each evaluator is assigned only once per project.
Junction Table:proyecto_estudiantesA project can have multiple student authors (team projects), and students can be associated with multiple projects over time. The junction table links students to their authored projects.
SELECT perf.id, perf.nombre AS student_name, perf.created_at AS registered_dateFROM perfiles perfINNER JOIN proyecto_estudiantes pe ON pe.estudiante_id = perf.idWHERE pe.proyecto_id = 'project-uuid-here' AND perf.rol = 'estudiante'ORDER BY perf.nombre;
Direct Relationship:evaluaciones.proyecto_id → proyectos.idEach project receives multiple evaluation records (one from each assigned evaluator). An evaluation belongs to exactly one project and is submitted by exactly one evaluator.
SELECT p.id, p.nombre AS project_name, AVG(e.puntaje_final) AS average_score, COUNT(e.id) AS total_evaluationsFROM proyectos pINNER JOIN evaluaciones e ON e.proyecto_id = p.idWHERE p.id = 'project-uuid-here'GROUP BY p.id, p.nombre;
Direct Relationship:evaluaciones.evaluador_id → perfiles.idEach evaluator (docente) can submit multiple evaluations over time. An evaluation record is authored by exactly one evaluator.
Example Query: All Evaluations by a Specific Evaluator
SELECT e.id, p.nombre AS project_name, p.categoria, e.puntaje_final, e.created_at AS submitted_atFROM evaluaciones eINNER JOIN proyectos p ON p.id = e.proyecto_idWHERE e.evaluador_id = 'evaluator-uuid-here'ORDER BY e.created_at DESC;
Fetch related data in a single query instead of N+1 queries:
// Good: Single query with joinsconst { data } = await supabaseClient .from('proyectos') .select('*, evaluaciones(*), proyecto_estudiantes(*)');// Avoid: Multiple separate queriesconst projects = await supabaseClient.from('proyectos').select('*');for (const p of projects) { const evals = await supabaseClient .from('evaluaciones') .select('*') .eq('proyecto_id', p.id);}
Index Foreign Keys
Ensure all foreign key columns are indexed for optimal JOIN performance:
CREATE INDEX idx_proyecto_evaluadores_proyecto_idON proyecto_evaluadores(proyecto_id);CREATE INDEX idx_proyecto_evaluadores_evaluador_idON proyecto_evaluadores(evaluador_id);CREATE INDEX idx_evaluaciones_proyecto_idON evaluaciones(proyecto_id);CREATE INDEX idx_evaluaciones_evaluador_idON evaluaciones(evaluador_id);
SELECT p.nombre AS project_name, perf.nombre AS evaluator_name, pe.created_at AS assigned_dateFROM proyectos pINNER JOIN proyecto_evaluadores pe ON pe.proyecto_id = p.idINNER JOIN perfiles perf ON perf.id = pe.evaluador_idWHERE p.estado = 'Pendiente';
SELECT p.nombre AS project_name, p.estado, COUNT(e.id) AS evaluation_count, AVG(e.puntaje_final) AS average_scoreFROM proyectos pLEFT JOIN evaluaciones e ON e.proyecto_id = p.idGROUP BY p.id, p.nombre, p.estado;