Skill para el Proyecto 2 de Sistemas de Bases de Datos 1 (SBD1) USAC 2026. Usar cuando el estudiante esté trabajando en la Dockerización de Oracle XE, API REST con Node.js/Express, consultas estadísticas SQL, o documentación del sistema de Centros de Evaluación de Manejo. Cubre: Docker/Docker Compose, Oracle XE, Node.js, Express, oracledb driver, Postman, DBeaver.
Curso: Sistemas de Bases de Datos 1, USAC — 1S 2026
Peso: 35.72 pts
Entrega: 30-04-2026 | Calificación: 02-05-2026 al 03-05-2026
Tablas del modelo (Oracle XE):
| Capa | Tecnología |
|---|---|
| Contenedor | Docker + Docker Compose |
| Base de datos | Oracle XE 21c |
| Backend |
| Node.js + Express |
| Driver BD | oracledb (npm) |
| Pruebas | Postman |
| Admin BD | DBeaver |
SBD1B_1S2026_#carnet/
├── docker-compose.yml
├── .env
├── README.md
├── oracle-db/
│ └── init-scripts/
│ ├── 01_ddl.sql ← Schema DDL completo
│ └── 02_dml.sql ← Datos de prueba
└── src/
├── app.js ← Entrada principal Express
├── config/
│ └── db.js ← Pool de conexión oracledb
└── routes/
├── departamento.js
├── municipio.js
├── centro.js
├── escuela.js
├── ubicacion.js
├── registro.js
├── correlativo.js
├── examen.js
├── preguntas.js
├── preguntasPractico.js
├── respuestaUsuario.js
├── respuestaPracticoUsuario.js
└── estadisticas.js ← Las 3 consultas estadísticas
const oracledb = require('oracledb');
oracledb.initOracleClient(); // thick mode si se necesita
async function getConnection() {
return await oracledb.getConnection({
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
connectString: process.env.DB_CONNECT_STRING
});
}
module.exports = { getConnection };
const router = require('express').Router();
const { getConnection } = require('../config/db');
// GET todos
router.get('/', async (req, res) => {
let conn;
try {
conn = await getConnection();
const result = await conn.execute('SELECT * FROM TABLA', [], { outFormat: oracledb.OUT_FORMAT_OBJECT });
res.json(result.rows);
} catch (err) {
res.status(500).json({ error: err.message });
} finally {
if (conn) await conn.close();
}
});
Consulta 1 — Estadísticas por centro y escuela:
SELECT
c.nombre AS centro,
e.nombre AS escuela,
COUNT(DISTINCT ex.id_examen) AS total_examenes,
ROUND(AVG(teorico.punteo), 2) AS promedio_teorico,
ROUND(AVG(practico.punteo), 2) AS promedio_practico,
COUNT(CASE WHEN (teorico.punteo + practico.punteo) >= 60 THEN 1 END) AS aprobados
FROM EXAMEN ex
JOIN CENTRO c ON ex.registro_id_centro = c.id_centro
JOIN ESCUELA e ON ex.registro_id_escuela = e.id_escuela
LEFT JOIN (subquery teorico) ON ...
LEFT JOIN (subquery practico) ON ...
GROUP BY c.nombre, e.nombre
Consulta 2 — Ranking de evaluados:
SELECT
r.nombre_completo,
SUM(teorico) + SUM(practico) AS puntaje_total,
RANK() OVER (ORDER BY (SUM(teorico) + SUM(practico)) DESC) AS ranking
FROM REGISTRO r JOIN EXAMEN ex ON ...
GROUP BY r.nombre_completo
ORDER BY ranking
Consulta 3 — Pregunta con menor aciertos:
SELECT
p.pregunta_texto,
COUNT(*) AS total_respuestas,
SUM(CASE WHEN ru.respuesta = p.respuesta_correcta THEN 1 ELSE 0 END) AS aciertos,
ROUND(SUM(CASE WHEN ru.respuesta = p.respuesta_correcta THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) AS porcentaje_aciertos
FROM PREGUNTAS p
JOIN RESPUESTA_USUARIO ru ON p.id_pregunta = ru.pregunta_id_pregunta
GROUP BY p.id_pregunta, p.pregunta_texto, p.respuesta_correcta
ORDER BY porcentaje_aciertos ASC
FETCH FIRST 1 ROW ONLY