Skip to content

Consultar la base de datos ​

QA usa SQL para una cosa muy concreta: verificar que lo que muestra la aplicación coincide con lo que hay guardado. Cuando no coinciden, has encontrado algo.

Cómo entrar ​

Por el navegador (recomendado) ​

Abre db.theproject.ec:

CampoValor
MotorPostgreSQL
Servidorpostgres-lab
Usuariostudent_ro
Contraseñalectura-qa-2026
Base de datosreservalab

No hay que instalar nada. Escribe la consulta en SQL command y ejecuta.

Con DBeaver o psql ​

Si prefieres una herramienta de escritorio, pídele al instructor el acceso: la base de datos no está abierta a internet directamente, por seguridad.

El usuario es de solo lectura ​

student_ro puede consultar todo pero no modificar nada. Si intentas:

sql
DELETE FROM appointments WHERE id = 1;

Obtendrás:

ERROR: permission denied for table appointments

Eso es correcto, no un problema del entorno. Los permisos son parte de cómo se protege una base de datos.

Las tablas ​

users
  id, email, password_hash, nombre, rol, created_at
  rol ∈ ('admin', 'cliente')

services
  id, nombre, descripcion, duracion_min, precio, activo, created_at

appointments
  id, user_id, service_id, fecha, estado, notas, created_at, cancelled_at
  estado ∈ ('confirmada', 'cancelada', 'completada')

Las relaciones: appointments.user_id → users.id y appointments.service_id → services.id.

Antes de nada: usa siempre LIMIT ​

No hagas SELECT * FROM appointments

La tabla de citas tiene más de doscientas mil filas. Si pides todas, el navegador tiene que dibujar doscientas mil filas y se queda colgado varios minutos. No es que la consola esté mal: es que le pediste demasiado.

sql
-- ❌ Mal: intenta traer la tabla entera
SELECT * FROM appointments;

-- ✅ Bien: mira lo que necesitas
SELECT * FROM appointments LIMIT 100;

-- ✅ Mejor todavía: filtra por lo que te interesa
SELECT * FROM appointments WHERE user_id = 9;

Esto no es una limitación del laboratorio, es el hábito correcto. En cualquier sistema real las tablas tienen millones de filas y un SELECT * sin límite bloquea la herramienta, satura la red y en el peor caso afecta al servicio en producción. Acostúmbrate desde ahora: primero LIMIT, después amplías.

Si solo quieres saber cuántas hay, no las traigas: cuéntalas.

sql
SELECT COUNT(*) FROM appointments;

Lo que necesitas saber de SQL ​

Seleccionar y filtrar ​

sql
SELECT id, nombre, precio
FROM services
WHERE activo = true
ORDER BY precio DESC;

Contar ​

sql
SELECT COUNT(*) FROM appointments;

Agrupar ​

sql
SELECT estado, COUNT(*) AS total
FROM appointments
GROUP BY estado
ORDER BY total DESC;

Unir tablas ​

Un JOIN combina filas de dos tablas por una columna en común:

sql
SELECT a.id, a.fecha, s.nombre AS servicio, u.email AS cliente
FROM appointments a
JOIN services s ON s.id = a.service_id
JOIN users u ON u.id = a.user_id
LIMIT 10;

JOIN vs LEFT JOIN — esto importa mucho

Un JOIN normal (inner) descarta las filas que no tienen pareja. Un LEFT JOIN las conserva.

Si añades una condición al JOIN:

sql
JOIN services s ON s.id = a.service_id AND s.activo = true

...estás eliminando del resultado todas las citas cuyo servicio esté inactivo, aunque esas citas existan. Es una de las formas más comunes de que datos reales "desaparezcan" de una pantalla. Tenlo en la cabeza.

Filtrar por fecha ​

sql
-- Las citas de un día concreto
SELECT * FROM appointments
WHERE fecha >= '2026-08-15'::date
  AND fecha <  '2026-08-16'::date;

-- Las futuras
SELECT * FROM appointments WHERE fecha > now();

Consultas útiles para el bootcamp ​

Todas tus citas ​

sql
SELECT a.id, a.fecha::date AS dia, a.estado, s.nombre AS servicio, s.activo
FROM appointments a
JOIN services s ON s.id = a.service_id
JOIN users u ON u.id = a.user_id
WHERE u.email = 'studentNN@theproject.ec'
ORDER BY a.id;

Comparar la API con la base de datos ​

Ejecuta GET /appointments y apunta el total. Después:

sql
SELECT COUNT(*)
FROM appointments a
JOIN users u ON u.id = a.user_id
WHERE u.email = 'studentNN@theproject.ec';

Si los números no coinciden, la aplicación te está ocultando datos. Averigua cuáles y por qué.

Servicios dados de baja ​

sql
SELECT id, nombre, activo FROM services ORDER BY id;

Citas huérfanas de un servicio inactivo ​

sql
SELECT a.id, a.estado, a.fecha::date, s.nombre, s.activo
FROM appointments a
JOIN services s ON s.id = a.service_id
WHERE s.activo = false;

Ver los índices de una tabla ​

Relevante para el módulo 4: un índice que falta es la causa más común de que una consulta vaya lenta.

sql
SELECT indexname, indexdef
FROM pg_indexes
WHERE tablename = 'appointments';

Ver el plan de ejecución ​

EXPLAIN ANALYZE te dice cómo ejecuta Postgres la consulta y cuánto tarda:

sql
EXPLAIN ANALYZE
SELECT COUNT(*) FROM appointments
WHERE fecha >= '2026-03-15'::date AND fecha < '2026-03-16'::date;

Lo que buscas en la salida:

ApareceSignifica
Index Scan / Index Only ScanUsa un índice. Bien.
Seq Scan / Parallel Seq ScanLee la tabla entera. Con muchas filas, mal.
Rows Removed by Filter: 199813Leyó 199.813 filas para descartarlas. Muy mal.

Errores típicos ​

ErrorCausa
relation "appointment" does not existLa tabla es appointments, en plural
permission denied for tableIntentaste escribir con el usuario de solo lectura
column "user" does not existEs user_id; y user es palabra reservada
operator does not exist: timestamp = textConvierte con ::date o ::timestamptz

Material del bootcamp de QA. El sistema de práctica contiene defectos a propósito.