🧪 Taller de Laboratorio
vistas

SQL en Vivo: PostgreSQL Real en tu Navegador

Esto no es un simulador: es PostgreSQL de verdad, compilado a WebAssembly, corriendo enteramente en tu navegador. Cada bloque de esta guía es editable y ejecutable — vas a crear el esquema de la guía de DDL con tus propias manos y ver los errores reales que devuelve el motor cuando algo sale mal.

RC
Ing. Roy Carrasco
Facultad de Ingeniería de Sistemas · UAB

Bienvenido

Este taller es la práctica de laboratorio de DDL y Restricciones en PostgreSQL. No vuelve a explicar la teoría desde cero — asume que ya la viste — y en cambio te da un PostgreSQL real para ejecutar cada sentencia y comprobar con tus propios ojos qué pasa.

📎 Prerrequisito

Si todavía no viste la teoría, repasa primero DDL y Restricciones en PostgreSQL antes de continuar. Este taller reconstruye exactamente el mismo esquema (CLIENTE, PRODUCTO, PEDIDO, DETALLE_PEDIDO) que esa guía.

¿Cómo es posible correr PostgreSQL en una página web?

Usa PGlite, una compilación del propio motor de PostgreSQL a WebAssembly (WASM). No es un motor distinto que imita la sintaxis de Postgres —como pasa con otras herramientas educativas basadas en SQLite— es el mismo PostgreSQL, corriendo dentro de tu navegador, sin instalar nada ni depender de ningún servidor.

⚠️ Todo vive en la memoria de esta pestaña

La base de datos que vas a construir existe solo mientras esta página siga abierta. Si recargas la página, cierras la pestaña, o navegas a otra guía, se pierde todo y hay que volver a ejecutar los bloques desde el principio. Si te equivocás y querés empezar de cero sin recargar, usa el botón "Reiniciar base de datos" de abajo.

✍️ Lo escribís vos

Cada bloque parte vacío. El enunciado te dice qué tenés que lograr — el código SQL lo escribís de cero.

🩻 Errores reales

Los mensajes de error que vas a ver son exactamente los que da PostgreSQL en producción.

🧩 Progresivo

Todos los ejercicios comparten la misma base de datos: lo que crees en el ejercicio 1 lo vas a usar en el 4.

La base de datos está vacía. Empieza por el ejercicio 1.

Zona de pruebas libres

Este bloque queda disponible durante toda la guía. Usalo en cualquier momento para probar una idea propia, revisar el estado de una tabla con SELECT * FROM tabla;, o experimentar con algo que se te ocurra a mitad de un ejercicio — sin desordenar los bloques guiados.

🧪 Escribe cualquier sentencia SQL

1. Tablas base: CLIENTE y PRODUCTO

Igual que en la guía teórica, empezamos por las dos tablas que no dependen de ninguna otra. Escribí vos mismo las sentencias CREATE TABLE y presioná ▶ Ejecutar para crearlas de verdad.

Enunciado
  • cliente(cliente_id clave primaria autoincremental, cliente_nombre VARCHAR(120) obligatorio)
  • producto(producto_id clave primaria autoincremental, producto_nombre VARCHAR(120) obligatorio, precio NUMERIC(10,2) obligatorio con un CHECK que exija que sea mayor a 0, activo BOOLEAN obligatorio con DEFAULT true)
🗄️ Ejercicio 1 — Crea las tablas base
Repite la ejecución

Presiona ▶ Ejecutar una segunda vez, sin cambiar nada. PostgreSQL te va a devolver un error real de tipo relation "cliente" already exists. Corrígelo agregando IF NOT EXISTS después de CREATE TABLE en ambas tablas, y ejecuta de nuevo para confirmar que ya no falla.

2. Relaciones y claves foráneas: PEDIDO y DETALLE_PEDIDO

Ahora las tablas que sí dependen de otras. Escribí las sentencias CREATE TABLE siguiendo el enunciado.

Enunciado
  • pedido(pedido_id clave primaria autoincremental, fecha DATE obligatoria con DEFAULT CURRENT_DATE, cliente_id obligatorio como clave foránea hacia cliente)
  • detalle_pedido(pedido_id clave foránea hacia pedido con ON DELETE CASCADE, producto_id clave foránea hacia producto con ON DELETE RESTRICT, cantidad obligatoria con un CHECK mayor a 0, y clave primaria compuesta por pedido_id y producto_id)
🗄️ Ejercicio 2 — Crea pedido y detalle_pedido
Probá el orden incorrecto

Bajá a la zona de pruebas libres y escribí a mano CREATE TABLE otra_tabla (id INTEGER REFERENCES tabla_inexistente(id));. Vas a ver el error real: relation "tabla_inexistente" does not exist — exactamente lo que predice la guía teórica sobre el orden de creación.

3. Restricciones en acción

Primero cargamos datos válidos. Después, cada bloque siguiente te pide una sola sentencia que va a fallar a propósito — así podés ver, aislado, el mensaje de error exacto que corresponde a cada tipo de restricción.

📎 Sintaxis de referencia: INSERT, UPDATE y DELETE

La guía teórica de DDL no cubre estas tres instrucciones — son DML (Data Manipulation Language), tema de la próxima guía. Para este taller te alcanza con la forma general:

SQL
INSERT INTO tabla (columna1, columna2) VALUES (valor1, valor2);

UPDATE tabla SET columna = valor WHERE condición;

DELETE FROM tabla WHERE condición;

De acá en adelante, cuando el enunciado diga "inserta", "actualiza" o "borra", adaptá esta forma general con los datos que te pide cada ejercicio.

Enunciado — datos válidos de partida

Inserta en cliente dos filas: 'Ana Gutiérrez' y 'Marco Flores'. Inserta en producto dos filas: 'Teclado mecánico' a 250.00 y 'Mouse inalámbrico' a 90.00.

🗄️ Datos válidos de partida

3.1 — Viola un CHECK: intenta insertar en producto un 'Cable USB' con precio -5.00.

💥 Ejercicio 3.1 — CHECK

3.2 — Viola un NOT NULL: intenta insertar un cliente con cliente_nombre en NULL.

💥 Ejercicio 3.2 — NOT NULL

3.3 — Viola una clave foránea: intenta insertar un pedido con un cliente_id que no existe (por ejemplo, 999).

💥 Ejercicio 3.3 — FOREIGN KEY
Fíjate en el patrón

Cada tipo de restricción tiene un mensaje de error distinto y reconocible: violates check constraint, violates not-null constraint y violates foreign key constraint. Aprender a reconocer estos tres patrones te va a ahorrar mucho tiempo depurando scripts reales.

4. CASCADE vs. RESTRICT en acción

Primero armamos un pedido completo con su detalle. Después probamos qué pasa al intentar borrar cada extremo de la relación.

Enunciado — 4.0

Inserta en pedido una fila para cliente_id = 1. Inserta en detalle_pedido dos filas para ese pedido: producto_id = 1 con cantidad = 3, y producto_id = 2 con cantidad = 1. Confirma con un SELECT * FROM detalle_pedido; al final.

🗄️ Ejercicio 4.0 — Crea un pedido con detalle

4.1 — Intenta borrar un producto referenciado (declarado con ON DELETE RESTRICT): borra el producto con producto_id = 1.

💥 Ejercicio 4.1 — RESTRICT

4.2 — Borra el pedido (declarado con ON DELETE CASCADE) con pedido_id = 1, y comprueba con un SELECT qué pasó con su detalle:

🗄️ Ejercicio 4.2 — CASCADE
La diferencia, en vivo

RESTRICT bloqueó el borrado de producto mientras siguiera referenciado. CASCADE dejó borrar pedido y arrastró automáticamente sus filas de detalle_pedido — el SELECT final debería devolver 0 filas.

5. ALTER TABLE en vivo

Una tabla ya creada no es definitiva. Agregamos una columna nueva a cliente y comprobamos qué valor toman las filas que ya existían.

Enunciado — 5.1

Agrega a cliente una columna email VARCHAR(150). Confirma con un SELECT * FROM cliente; al final.

🗄️ Ejercicio 5.1 — Agrega una columna
Enunciado — 5.2

Agrega una restricción UNIQUE llamada cliente_email_unico sobre la columna email. Luego actualiza el cliente cliente_id = 1 para que su email sea '[email protected]'.

🗄️ Ejercicio 5.2 — ADD CONSTRAINT UNIQUE

5.3 — Intenta repetir el mismo correo en otro cliente: actualiza cliente_id = 2 con el mismo email '[email protected]'.

💥 Ejercicio 5.3 — UNIQUE

6. DROP TABLE y sus dos CASCADE

Hay dos cosas distintas en esta guía que se llaman "CASCADE", y es fácil confundirlas. Vamos a verlas una junto a la otra.

6.1 — Intenta borrar producto sin CASCADE: ejecuta un DROP TABLE simple sobre producto.

💥 Ejercicio 6.1 — DROP TABLE bloqueado

6.2 — Ahora con CASCADE: vuelve a borrar producto, esta vez con CASCADE, y comprueba con un SELECT qué le pasó a detalle_pedido.

🗄️ Ejercicio 6.2 — DROP TABLE ... CASCADE
Dos CASCADE que no son lo mismo

ON DELETE CASCADE (sección 4) es una regla que vive dentro de una fila: borra automáticamente las filas hijas cuando se borra la fila padre. DROP TABLE ... CASCADE (esta sección) es una operación de estructura: borra la restricción de clave foránea que dependía de la tabla eliminada, pero no borra la tabla ni las filas de detalle_pedido — el SELECT final debería seguir mostrando datos, solo que detalle_pedido ya no tiene ninguna clave foránea apuntando a producto.

Antes de seguir al reto final

El esquema quedó parcialmente desarmado a propósito. Usa el botón 🔄 Reiniciar base de datos al principio de la guía para empezar el reto siguiente con una base de datos completamente vacía.

Reto final: diseña tu propio esquema

Con la base de datos reiniciada, diseña y crea desde cero el esquema de una biblioteca, aplicando todo lo practicado en este taller.

Requisitos
  • autor(autor_id, autor_nombre)
  • libro(libro_id, titulo, autor_id → referencia a autor, anio_publicacion con un CHECK razonable)
  • socio(socio_id, socio_nombre, email con UNIQUE)
  • prestamo(libro_id, socio_id — clave primaria compuesta, fecha_prestamo con DEFAULT CURRENT_DATE, fecha_devolucion que pueda quedar sin definir)
  • Decide vos la acción referencial (CASCADE, RESTRICT o SET NULL) de cada clave foránea de prestamo, y justifica tu elección con un comentario -- en el propio bloque.

Una vez creadas las tablas, insertá un par de filas de cada una y confirmalas con SELECT * FROM tabla; — todavía no hace falta que domines SELECT a fondo, eso es tema de la próxima guía.

🏆 Resuelve aquí el reto

Repaso

Cuatro preguntas cortas sobre lo que acabas de observar en vivo. Tu progreso se guarda automáticamente en este navegador.

0 / 0
actividades correctas en toda la guía
Selección múltiple

Al insertar un producto con precio -5.00, PostgreSQL respondió con un error que menciona "violates check constraint". ¿Qué restricción provocó ese error?

Verdadero o falso

En el ejercicio 4, borrar el producto referenciado por detalle_pedido funcionó sin problema porque PostgreSQL siempre permite borrar cualquier fila.

Selección múltiple

Después de DROP TABLE producto CASCADE, ¿qué pasó con la tabla detalle_pedido?

Clasificación

¿Es una regla de fila (ON DELETE ...) o una operación de estructura (DROP TABLE ...)?

Al borrar un pedido, sus filas de detalle_pedido se borran automáticamente.
Al eliminar la tabla producto con CASCADE, se elimina la restricción de clave foránea que apuntaba a ella.

Cheat Sheet del taller

SituaciónMensaje real de PostgreSQL
Crear una tabla que ya existerelation "..." already exists
Referenciar una tabla que aún no existerelation "..." does not exist
Violar un CHECKnew row for relation "..." violates check constraint "..."
Violar NOT NULLnull value in column "..." violates not-null constraint
Violar UNIQUE o PRIMARY KEY duplicadaduplicate key value violates unique constraint "..."
Insertar una FK que no existe en la tabla padreviolates foreign key constraint "...", detalle "Key (...) is not present in table "..."."
Borrar una fila padre protegida por RESTRICTupdate or delete on table "..." violates foreign key constraint "..." on table "..."
DROP TABLE sin CASCADE, con dependientescannot drop table "..." because other objects depend on it

¿Sabías que...?

🐘 PGlite es Postgres de verdad

A diferencia de motores educativos basados en SQLite (como sql.js), PGlite compila el código fuente real de PostgreSQL a WebAssembly: los mismos mensajes de error y el mismo comportamiento que en un servidor de producción.

📦 Menos de 4 MB

El motor completo de PostgreSQL comprimido para WebAssembly pesa alrededor de 3-4 MB — comparable a una foto de celular, y corre enteramente sin conexión una vez cargado.

🌳 PostgreSQL es más viejo que la Web

El proyecto POSTGRES nació en Berkeley en 1986, ocho años antes de que existiera el primer navegador gráfico — y hoy ese mismo motor corre dentro de uno.