lección 10
Modificar datos: INSERT, UPDATE y DELETE
Crear tablas, insertar filas, actualizar datos y borrar registros — el otro lado de SQL más allá de las consultas.
⏱ 50 min
Hasta ahora has sido un lector: SELECT, FROM, WHERE, JOIN... siempre consultando datos que ya existían. Pero en tu trabajo real como ingeniero de datos, también vas a CREAR estructuras (CREATE TABLE), INSERTAR datos (INSERT), MODIFICAR registros (UPDATE) y ELIMINAR filas (DELETE). Estas son las operaciones DML (Data Manipulation Language) y DDL (Data Definition Language) que completan tu arsenal de SQL.
La analogía: hasta ahora has sido un bibliotecario que busca y cataloga libros. Hoy te conviertes en el que también escribe libros nuevos, actualiza las fichas y retira los que están obsoletos. Es un poder mayor — y con mayor poder viene mayor responsabilidad. Un DELETE sin WHERE puede borrar toda la tabla. Un UPDATE sin WHERE puede sobrescribir todos los registros.
### CREATE TABLE: definir la estructura
Antes de insertar datos, necesitas una tabla con su esquema definido. CREATE TABLE define el nombre, las columnas y los tipos de cada columna. Es como crear la plantilla de un formulario antes de rellenarlo:
1-- Crear una tabla de reseñas de productos2CREATE TABLE resenas (3 id INTEGER PRIMARY KEY,4 producto_id INTEGER,5 cliente_id INTEGER,6 puntuacion INTEGER, -- 1 a 5 estrellas7 comentario VARCHAR,8 fecha DATE DEFAULT CURRENT_DATE9);1011-- Verificar que se creó12DESCRIBE resenas;
CREATE TABLE: defines columnas, tipos y restricciones
### Tipos de datos principales en DuckDB
- INTEGER — números enteros (id, cantidad, stock)
- DOUBLE — números decimales (precio, importe, porcentaje)
- VARCHAR — texto de longitud variable (nombre, email, descripción)
- BOOLEAN — TRUE/FALSE (es_vip, activo, verificado)
- DATE — fecha sin hora (2024-03-15)
- TIMESTAMP — fecha + hora (2024-03-15 14:30:00)
- PRIMARY KEY — restricción: valor único y no NULL
- DEFAULT — valor por defecto si no se especifica al insertar
### INSERT: añadir datos a la tabla
INSERT INTO añade filas nuevas a una tabla existente. Puedes insertar una fila, varias a la vez, o incluso el resultado de un SELECT:
1-- Insertar una fila2INSERT INTO resenas (id, producto_id, cliente_id, puntuacion, comentario)3VALUES (1, 42, 100, 5, 'Excelente producto, llegó en 2 días');45-- Insertar múltiples filas de una vez6INSERT INTO resenas (id, producto_id, cliente_id, puntuacion, comentario) VALUES7 (2, 42, 200, 4, 'Buena calidad, algo caro'),8 (3, 15, 100, 3, 'Normal, esperaba más'),9 (4, 88, 300, 5, 'Perfecto para lo que necesitaba'),10 (5, 15, 400, 1, 'Llegó roto, pido devolución');1112-- Verificar13SELECT * FROM resenas;
INSERT: una fila o varias con VALUES, siempre especificando columnas
Siempre lista las columnas en el INSERT: INSERT INTO tabla (col1, col2) VALUES (...). Aunque es más largo que INSERT INTO tabla VALUES (...), te protege si alguien añade una columna nueva a la tabla — tu INSERT sigue funcionando.
### INSERT ... SELECT: crear datos a partir de una query
Uno de los patrones más poderosos en ingeniería de datos: crear una tabla nueva con el resultado de una consulta. Es la base de cualquier pipeline ETL (Extract, Transform, Load):
1-- CREATE TABLE AS: crear tabla directamente desde una query2CREATE TABLE resumen_clientes AS3SELECT4 c.id,5 c.nombre,6 c.ciudad,7 COUNT(p.id) AS total_pedidos,8 SUM(p.importe)::INTEGER AS gasto_total,9 MAX(p.fecha) AS ultimo_pedido10FROM clientes c11LEFT JOIN pedidos p ON p.cliente_id = c.id AND p.estado = 'completado'12GROUP BY c.id, c.nombre, c.ciudad;1314-- Verificar15SELECT * FROM resumen_clientes ORDER BY gasto_total DESC LIMIT 5;
CREATE TABLE AS SELECT: el patrón ETL más básico — transformar y guardar
Ojo con CREATE TABLE AS SELECT: parece magia (creo una tabla con una query y listo), pero tiene truco. La nueva tabla hereda SOLO los tipos de las columnas — nada de PRIMARY KEY, NOT NULL, UNIQUE ni ningún constraint de la tabla original. He visto a gente confiar en una tabla creada con CTAS como si fuera la original y luego descubrir duplicados que la PRIMARY KEY habría prevenido. Si necesitas constraints, añádelos después con ALTER TABLE.
### UPDATE: modificar datos existentes
UPDATE cambia valores de filas existentes. La cláusula WHERE es OBLIGATORIA (moralmente, no sintácticamente) — sin WHERE, actualiza TODAS las filas:
1-- Actualizar la puntuación de una reseña específica2UPDATE resenas3SET puntuacion = 4, comentario = 'Actualizo: al final funciona bien'4WHERE id = 5;56-- Verificar que SOLO cambió esa fila7SELECT * FROM resenas WHERE id = 5;89-- PATRÓN SEGURO: primero SELECT para verificar, luego UPDATE10-- Paso 1: ver qué filas afectaría11SELECT * FROM resenas WHERE puntuacion < 3;12-- Paso 2: si son las correctas, ejecutar el UPDATE13UPDATE resenas SET comentario = comentario || ' [revisado]' WHERE puntuacion < 3;
UPDATE siempre con WHERE. Sin WHERE = desastre garantizado.
REGLA DE ORO en producción: NUNCA ejecutes un UPDATE sin antes hacer el mismo WHERE en un SELECT. Si el SELECT devuelve las filas que esperas, entonces ejecuta el UPDATE. He visto equipos enteros quedarse sin comer un viernes porque alguien hizo UPDATE sin WHERE en la tabla de clientes.
### DELETE: eliminar filas
DELETE FROM elimina filas que cumplen la condición. Misma regla que UPDATE: SIEMPRE con WHERE, y siempre verificando antes con SELECT:
1-- Paso 1: verificar qué vamos a borrar2SELECT * FROM resenas WHERE puntuacion = 1;34-- Paso 2: borrar solo esas filas5DELETE FROM resenas WHERE puntuacion = 1;67-- Verificar que se borró8SELECT COUNT(*) AS restantes FROM resenas;910-- PELIGRO: esto borra TODO (sin WHERE)11-- DELETE FROM resenas; ← NUNCA sin WHERE en producción
DELETE: primero SELECT para ver, luego DELETE para borrar
### DROP TABLE vs DELETE FROM
No confundas DELETE (borra filas, la tabla sigue existiendo) con DROP TABLE (destruye la tabla completa, estructura incluida):
1-- DELETE: borra filas, la tabla sigue existiendo (vacía)2DELETE FROM resenas WHERE id > 3;34-- DROP TABLE: destruye la tabla completamente5DROP TABLE IF EXISTS resumen_clientes;67-- IF EXISTS evita error si la tabla no existe8DROP TABLE IF EXISTS tabla_que_no_existe; -- no da error
DELETE borra contenido. DROP TABLE borra la tabla entera.
### Patrón profesional: tablas staging
En pipelines reales, rara vez modificas tablas originales directamente. El patrón es: crear una tabla staging con los datos transformados, verificar que está bien, y luego reemplazar la original. Es más seguro que un UPDATE masivo:
1-- Patrón staging: crear nueva versión de la tabla2CREATE TABLE clientes_limpio AS3SELECT4 id,5 nombre,6 COALESCE(ciudad, 'Sin asignar') AS ciudad,7 email,8 fecha_registro,9 es_vip10FROM clientes11WHERE nombre IS NOT NULL;1213-- Verificar14SELECT COUNT(*) FROM clientes_limpio;1516-- Si todo bien, podrías reemplazar (en producción con transacción)17-- DROP TABLE clientes;18-- ALTER TABLE clientes_limpio RENAME TO clientes;
Crear tabla staging → verificar → reemplazar. Más seguro que UPDATE masivo.
En tu carrera, la mayoría de tus INSERT serán INSERT ... SELECT (mover datos transformados de una tabla a otra) y CREATE TABLE AS (crear tablas de resumen). Los INSERT VALUES manuales son para testing o datos de configuración.
### Transacciones: el botón de "deshacer" de SQL
Todo lo anterior (INSERT, UPDATE, DELETE) es irreversible una vez ejecutado... a menos que uses transacciones. Una transacción agrupa varias operaciones en un bloque atómico: o se ejecutan TODAS o no se ejecuta NINGUNA. Si algo falla a mitad, todo se deshace automáticamente.
1-- Sin transacción: si el paso 2 falla, el paso 1 ya se ejecutó (datos inconsistentes)2DELETE FROM pedidos_antiguos WHERE fecha < '2020-01-01';3INSERT INTO archivo_pedidos SELECT * FROM pedidos_antiguos WHERE fecha < '2020-01-01';45-- Con transacción: si algo falla, TODO se deshace6BEGIN;7 INSERT INTO archivo_pedidos SELECT * FROM pedidos WHERE fecha < '2020-01-01';8 DELETE FROM pedidos WHERE fecha < '2020-01-01';9COMMIT;1011-- Si te arrepientes ANTES del COMMIT:12-- ROLLBACK; ← deshace todo lo que hiciste desde BEGIN
BEGIN...COMMIT agrupa operaciones. Si algo falla, ROLLBACK deshace todo.
Consejo de senior: en mi primer mes como junior borré 50.000 pedidos de producción con un DELETE sin WHERE. No había transacción. Tuvimos que restaurar un backup de 4 horas antes y perdimos datos de medio día. Desde entonces, SIEMPRE envuelvo cualquier DELETE o UPDATE en producción con BEGIN/COMMIT. Los 5 segundos que tardas en escribir BEGIN te pueden ahorrar el peor día de tu carrera.
## ejercicios
Crear tabla de reseñas
Crea una tabla llamada "resenas" con columnas: id (INTEGER PRIMARY KEY), producto_id (INTEGER), cliente_id (INTEGER), puntuacion (INTEGER), comentario (VARCHAR), fecha (DATE con default CURRENT_DATE). Luego describe la tabla para verificar.
Insertar reseñas de ejemplo
Inserta 5 reseñas en la tabla (créala primero). Variedad: puntuaciones de 1 a 5, diferentes productos y clientes. Luego muestra todas las filas ordenadas por puntuación descendente.
💡 Resultado esperado
5 filas ordenadas por puntuación (5→1): id=1 prod=42 punt=5 | id=2 prod=15 punt=4 | id=3 prod=88 punt=3 | id=4 prod=42 punt=2 | id=5 prod=15 punt=1
UPDATE seguro con verificación
Crea la tabla resenas con datos. Luego usa el patrón seguro: primero SELECT para ver las reseñas con puntuación < 3, luego UPDATE para añadir " [ATENCIÓN]" al final de su comentario. Finalmente muestra el resultado.
💡 Resultado esperado
5 filas · los comentarios con puntuación < 3 terminan en " [ATENCIÓN]": id=3 punt=1 "Malo [ATENCIÓN]" | id=2 punt=2 "Regular [ATENCIÓN]" | id=5 punt=2 "No lo recomiendo [ATENCIÓN]" | id=4 punt=4 "Muy bien" | id=1 punt=5 "Perfecto"
Limpiar pedidos cancelados
Crea una tabla pedidos_copia como copia de los primeros 100 pedidos. Luego cuenta cuántos están cancelados, elimínalos con DELETE, y verifica que ya no quedan cancelados.
Crear tabla resumen con CREATE TABLE AS
Crea una tabla "resumen_ciudades" usando CREATE TABLE AS SELECT. Debe contener: ciudad, total_clientes, clientes_vip, total_pedidos_completados y facturacion_total. Luego consulta el resumen ordenado por facturación.
Regístrate para guardar tu progreso.
## comentarios
Reporta erratas, ayuda a otros o comparte tu opinión. Sé constructivo.
Inicia sesión para comentar y responder.
cargando comentarios...