🗄️ Guía de Aula Invertida
vistas

DML y SELECT básico en PostgreSQL

Tus tablas ya existen, con sus restricciones y claves foráneas. Ahora toca llenarlas, modificarlas, vaciarlas cuando corresponda, y sobre todo: sacarles información. INSERT, UPDATE, DELETE y las primeras consultas SELECT con WHERE, ORDER BY y LIMIT.

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

Bienvenido

En la guía anterior creaste cliente, producto, pedido y detalle_pedido con CREATE TABLE, sus restricciones y sus claves foráneas. Esas tablas están vacías: el DDL define la estructura, pero no mete un solo dato adentro. Eso es trabajo del DML (Data Manipulation Language): INSERT, UPDATE y DELETE para manipular filas, y SELECT para leerlas.

Objetivo del módulo

Al finalizar esta guía sabrás insertar filas con INSERT INTO (una o varias a la vez), modificar filas existentes con UPDATE, eliminarlas con DELETE sin borrar la tabla completa por accidente, y escribir tus primeras consultas SELECT filtrando con WHERE, ordenando con ORDER BY y limitando resultados con LIMIT.

📝 Actividades de este módulo

Encontrarás actividades cortas y autocorregibles —selección múltiple, verdadero/falso, emparejamiento, clasificación y completar espacios— repartidas a lo largo de la guía, y una sección de ejercicios de razonamiento donde se te da un escenario y debes predecir el resultado correcto antes de comprobarlo. Cada actividad te da retroalimentación inmediata.

➕ Inserta

Filas nuevas en el esquema que ya construiste: clientes, productos, pedidos y sus detalles.

✏️ Modifica y borra

UPDATE y DELETE, y por qué el WHERE nunca es opcional en la práctica.

🔎 Consulta

SELECT con WHERE, ORDER BY y LIMIT: la base de toda consulta más compleja que vendrá después.

INSERT INTO

INSERT INTO agrega filas nuevas a una tabla. La forma más clara siempre indica explícitamente qué columnas está llenando.

SQL
INSERT INTO cliente (cliente_nombre)
VALUES ('Ana Rojas');

INSERT INTO producto (producto_nombre, precio)
VALUES ('Teclado mecánico', 250.00);

Fíjate que cliente_id y producto_id no aparecen: son GENERATED ALWAYS AS IDENTITY, PostgreSQL los genera solos. Tampoco aparece activo en producto: al no especificarlo, se usa su DEFAULT true.

¿Por qué listar las columnas siempre?

INSERT INTO producto VALUES (1, 'Mouse', 80.00, true) funciona, pero depende del orden exacto de columnas de la tabla. Si alguien agrega una columna nueva en medio con ALTER TABLE, ese INSERT se rompe o —peor— inserta datos en la columna equivocada sin avisar. Escribir INSERT INTO producto (producto_nombre, precio) VALUES (...) es inmune a ese problema.

Un solo INSERT puede cargar varias filas a la vez, separando los grupos de valores con comas:

SQL
INSERT INTO producto (producto_nombre, precio) VALUES
    ('Mouse óptico', 80.00),
    ('Monitor 24"', 620.00),
    ('Silla ergonómica', 890.00);

Es una sola instrucción, no tres: si una fila viola una restricción (por ejemplo un CHECK (precio > 0)), ninguna de las tres se inserta.

RETURNING: recuperar el id generado

Como cliente_id lo genera PostgreSQL, ¿cómo sabes qué id le tocó a la fila que acabas de insertar? Con RETURNING, una cláusula propia de PostgreSQL que devuelve datos de la fila insertada en el mismo INSERT:

SQL
INSERT INTO cliente (cliente_nombre)
VALUES ('Marco Vidal')
RETURNING cliente_id;
Selección múltiple

La tabla producto tiene: producto_id (IDENTITY), producto_nombre (NOT NULL), precio (NOT NULL, CHECK > 0), activo (DEFAULT true). ¿Cuál INSERT funciona sin error?

Completar

Sobre la instrucción: INSERT INTO cliente (cliente_nombre) VALUES ('Ana Rojas') RETURNING cliente_id;

1. La palabra clave que indica en qué tabla se inserta es .

2. La cláusula que hace que PostgreSQL devuelva el cliente_id recién generado es .

UPDATE

UPDATE modifica filas que ya existen. Se compone de tres partes: la tabla, las columnas nuevas con SET, y —casi siempre— un WHERE que decide a cuáles filas aplica.

SQL
UPDATE producto
SET precio = 95.00
WHERE producto_id = 2;

Puedes actualizar varias columnas a la vez separándolas con comas, e incluso usar el valor actual de la columna en la expresión nueva:

SQL
UPDATE producto
SET precio = precio * 1.10, activo = true
WHERE producto_nombre = 'Silla ergonómica';
El WHERE no es opcional en la práctica

UPDATE producto SET precio = precio * 1.10; sin WHERE es sintácticamente válido — y aumenta el precio del 100% de las filas de la tabla, no solo de la que querías. PostgreSQL no pregunta "¿estás seguro?": ejecuta exactamente lo que escribiste. Antes de correr un UPDATE, es buena práctica probar el mismo WHERE en un SELECT primero, para confirmar que selecciona exactamente las filas esperadas.

Verdadero o falso

Ejecutas: UPDATE cliente SET cliente_nombre = 'Sin nombre'; (sin cláusula WHERE). El resultado es que ninguna fila cambia, porque PostgreSQL exige un WHERE para identificar a qué fila aplica.

DELETE

DELETE FROM elimina filas completas. Igual que UPDATE, casi siempre necesita un WHERE para no borrar más de lo debido.

SQL
DELETE FROM detalle_pedido
WHERE pedido_id = 5 AND producto_id = 3;

DELETE FROM producto
WHERE activo = false;
DELETE sin WHERE ≠ DROP TABLE

DELETE FROM producto; sin WHERE borra todas las filas de producto, pero la tabla en sí —su estructura, columnas y restricciones— sigue existiendo, vacía. Es distinto de DROP TABLE producto;, que elimina la tabla por completo. Y también distinto de TRUNCATE TABLE producto;, una instrucción especial de PostgreSQL que vacía la tabla igual que un DELETE sin WHERE, pero es más rápida (no revisa fila por fila) y reinicia los contadores de columnas IDENTITY.

El comportamiento de un DELETE sobre las tablas relacionadas depende de las acciones ON DELETE definidas en la clave foránea, tal como viste en la guía de DDL: si borras un pedido, sus filas en detalle_pedido se borran en cascada (ON DELETE CASCADE); si intentas borrar un producto que ya tiene ventas, PostgreSQL lo rechaza (ON DELETE RESTRICT).

Clasificación

Clasifica cada instrucción según lo que le pasa a la tabla producto y a sus filas.

DELETE FROM producto WHERE producto_id = 4;
DELETE FROM producto;
DROP TABLE producto;

SELECT básico

SELECT es la instrucción de lectura: no modifica nada, solo devuelve filas. La forma más simple indica qué columnas quieres y de qué tabla.

SQL
SELECT cliente_nombre FROM cliente;

SELECT producto_nombre, precio FROM producto;

SELECT * FROM producto;

* significa "todas las columnas". Es cómodo mientras exploras datos en clase, pero en código real conviene evitarlo: si alguien agrega una columna nueva a la tabla, un SELECT * empieza a devolver más datos de los que la aplicación esperaba.

Alias con AS

Puedes renombrar una columna en el resultado (sin cambiar su nombre real en la tabla) con AS: SELECT precio AS precio_unitario FROM producto;. Útil para que los resultados sean más legibles, especialmente con columnas calculadas.

SQL
SELECT DISTINCT activo FROM producto;

DISTINCT elimina filas duplicadas del resultado. Sobre la columna activo (que solo puede ser true o false), esta consulta devuelve como máximo dos filas, sin importar cuántos productos existan.

Selección múltiple

La tabla pedido tiene 200 filas, con solo 3 valores distintos de fecha. ¿Cuántas filas devuelve SELECT DISTINCT fecha FROM pedido;?

WHERE y operadores

WHERE filtra qué filas se incluyen en el resultado (o a cuáles aplica un UPDATE/DELETE). La condición se evalúa fila por fila.

OperadorSignificado
=Igual a.
<> o !=Distinto de.
> < >= <=Mayor, menor, mayor o igual, menor o igual.
AND / OR / NOTCombinan varias condiciones. AND exige ambas, OR exige al menos una, NOT la invierte.
BETWEEN x AND yEstá entre x e y, ambos límites incluidos.
IN (lista)Coincide con alguno de los valores de la lista.
LIKE 'patrón'Coincide con un patrón de texto: % = cualquier cantidad de caracteres, _ = exactamente un caracter.
IS NULL / IS NOT NULLLa columna no tiene valor / sí tiene valor.
SQL
SELECT producto_nombre, precio FROM producto
WHERE precio BETWEEN 50 AND 300 AND activo = true;

SELECT producto_nombre FROM producto
WHERE producto_nombre LIKE 'Mouse%';

SELECT cliente_nombre FROM cliente
WHERE cliente_id IN (1, 4, 7);
Por qué "= NULL" nunca funciona

WHERE correo = NULL no selecciona nada, nunca, aunque haya filas con correo vacío. Para SQL, NULL representa "valor desconocido", y comparar cualquier cosa contra un desconocido da como resultado otro desconocido (no verdadero). Por eso existen IS NULL e IS NOT NULL como operadores aparte: son la única forma correcta de preguntar por la ausencia de un valor.

Precedencia: AND antes que OR

En WHERE activo = true AND precio > 500 OR precio < 10, PostgreSQL evalúa el AND primero, como si dijeras "(activo Y caro) O barato" — probablemente no es lo que querías. Usa paréntesis siempre que combines AND y OR: WHERE activo = true AND (precio > 500 OR precio < 10).

Emparejamiento

Une cada condición con lo que selecciona.

ORDER BY y LIMIT

Por defecto, PostgreSQL no garantiza ningún orden particular en el resultado de un SELECT. Si el orden importa, hay que pedirlo explícitamente.

SQL
SELECT producto_nombre, precio FROM producto
ORDER BY precio DESC;

SELECT producto_nombre, precio FROM producto
ORDER BY activo DESC, precio ASC;

ASC (ascendente, de menor a mayor) es el orden por defecto si no escribes nada. DESC invierte el orden. Cuando ordenas por varias columnas, la primera manda; la segunda solo desempata entre filas iguales en la primera.

SQL
SELECT producto_nombre, precio FROM producto
ORDER BY precio DESC
LIMIT 3;

SELECT producto_nombre, precio FROM producto
ORDER BY precio DESC
LIMIT 3 OFFSET 3;

LIMIT corta el resultado a las primeras N filas (según el orden aplicado). OFFSET salta las primeras N filas antes de empezar a contar — la combinación de ambos es la técnica clásica de paginación: la segunda consulta trae "la segunda página" de 3 productos, saltándose los 3 más caros que ya se mostraron.

LIMIT sin ORDER BY es una trampa

SELECT * FROM producto LIMIT 5; sin ORDER BY te da 5 filas cualesquiera — PostgreSQL no promete cuáles, ni que sean siempre las mismas entre una ejecución y otra. Si necesitas "los 5 más caros" o "los 5 más recientes", el ORDER BY correcto no es opcional, es parte de la pregunta que estás haciendo.

Completar

Quieres los 2 productos más baratos de la tabla.

1. Para ordenar de menor a mayor precio, usas ORDER BY precio .

2. Para quedarte solo con los primeros 2 resultados, agregas .

Ejercicios de razonamiento

En estas preguntas no se te pide recordar sintaxis: se te da un escenario, y debes razonar cuál sería la respuesta correcta antes de comprobarla.

Escenario

Quieres registrar un pedido nuevo para el cliente con cliente_id = 3, con la fecha de hoy (que ya tiene DEFAULT CURRENT_DATE en la tabla), y necesitas conocer el pedido_id que PostgreSQL le asigne para usarlo enseguida en un INSERT sobre detalle_pedido.

Razonamiento

¿Cuál INSERT es el correcto?

Otro escenario

Un producto se dejó de vender ("Cable HDMI", producto_id = 8) pero ya tiene ventas registradas en detalle_pedido (la clave foránea de producto_id ahí tiene ON DELETE RESTRICT). Igual necesitas que deje de aparecer en el catálogo de productos disponibles.

Razonamiento

¿Qué instrucción resuelve el problema sin generar un error?

Un último escenario

Quieres mostrar los 5 pedidos más recientes del cliente con cliente_id = 3, del más nuevo al más viejo.

Completar

Completa la consulta: SELECT * FROM pedido WHERE cliente_id = 3

ORDER BY fecha ;

Actividades de repaso

Un repaso integrador de todo el módulo. Cada actividad se corrige al instante; tu progreso se guarda automáticamente en este navegador.

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

Ejecutas un INSERT con varias filas en un solo VALUES, y la tercera fila viola un CHECK. ¿Qué ocurre?

Verdadero o falso

DELETE FROM cliente; (sin WHERE) elimina la tabla cliente completa, igual que DROP TABLE cliente;.

Selección múltiple

¿Cuál condición selecciona correctamente los pedidos que todavía no tienen fecha_entrega registrada?

Selección múltiple

¿Qué hace exactamente ORDER BY precio DESC LIMIT 1;?

Selección múltiple

¿Por qué conviene probar la condición de un UPDATE o DELETE con un SELECT antes de ejecutarlo?

Cheat Sheet

InstrucciónPara qué sirve
INSERT INTO tabla (cols) VALUES (...)Agrega una o varias filas nuevas.
... RETURNING columnaDevuelve datos de la fila insertada (útil para ids generados).
UPDATE tabla SET col = valor WHERE ...Modifica filas existentes que cumplen la condición.
DELETE FROM tabla WHERE ...Elimina filas que cumplen la condición (sin WHERE, elimina todas).
SELECT cols FROM tablaConsulta columnas específicas (o * para todas).
DISTINCTElimina filas duplicadas del resultado.
WHERE (=, <>, AND/OR, BETWEEN, IN, LIKE, IS NULL)Filtra qué filas se incluyen.
ORDER BY col ASC/DESCOrdena el resultado (ASC por defecto).
LIMIT n OFFSET mCorta el resultado a n filas, saltando las primeras m (paginación).

¿Sabías que...?

🕰️ SELECT es más viejo que "SQL"

El lenguaje se llamó originalmente SEQUEL (Structured English Query Language), diseñado por Donald Chamberlin y Raymond Boyce en IBM a inicios de los 70. Tuvo que renombrarse a SQL por un conflicto de marca registrada con otra empresa que ya usaba "SEQUEL".

🔒 UPDATE y DELETE también respetan transacciones

Igual que el DDL en PostgreSQL, un UPDATE o DELETE ejecutado dentro de una transacción puede deshacerse con ROLLBACK mientras no se haga COMMIT. Es la red de seguridad real contra un WHERE mal escrito: si te das cuenta a tiempo, todavía puedes deshacerlo.

📄 LIMIT/OFFSET no es el único método de paginación

Es el más simple, pero en tablas muy grandes OFFSET se vuelve lento porque PostgreSQL igual tiene que recorrer y descartar las filas saltadas. Motores y aplicaciones a gran escala suelen preferir "paginación por cursor" (seguir desde el último id visto), un tema que retomarás cuando la guía llegue a índices y optimización.