📘 Laboratorio 2: DML (Data Manipulation Language) - Caso Aletza

🎯 Objetivo

Aplicar los conceptos de DML (SELECT, INSERT, UPDATE, DELETE, MERGE) al caso del asistente virtual Aletza, utilizando los datos insertados en el Laboratorio 1.


📋 Requisitos Previos

  • Tener ejecutado el Laboratorio 1 con todas las tablas creadas y datos insertados
  • Conectado a Supabase con el SQL Editor abierto

🔍 Paso 1: SELECT - Consultas Básicas

1.1 SELECT con todas las columnas

-- Seleccionar todos los usuarios
SELECT * FROM usuarios;

-- Seleccionar todos los perfiles de voz
SELECT * FROM perfiles_voz;

-- Seleccionar todas las sesiones
SELECT * FROM sesiones;

-- Seleccionar todos los mensajes
SELECT * FROM mensajes;

-- Seleccionar todos los logs
SELECT * FROM logs_ejecucion;

1.2 SELECT con columnas específicas

-- Mostrar solo username y email de usuarios
SELECT username, email FROM usuarios;

-- Mostrar usuario_id, estado y muestras_tomadas de perfiles de voz
SELECT usuario_id, estado, muestras_tomadas FROM perfiles_voz;

-- Mostrar token, canal y expira_en de sesiones
SELECT token, canal, expira_en FROM sesiones;

-- Mostrar contenido y modalidad de mensajes
SELECT contenido, modalidad FROM mensajes;

-- Mostrar modulo, tiempo_ejecucion_ms y exito de logs
SELECT modulo, tiempo_ejecucion_ms, exito FROM logs_ejecucion;

-- Concatenación: "Token [canal] expira en: {expira_en}"
SELECT 
    'Token ' || token || ' [' || canal || '] expira en: ' || expira_en AS info_sesion
FROM sesiones;

1.3 SELECT con ALIAS (AS)

-- Usar alias para renombrar columnas
SELECT 
    username AS "Nombre de Usuario",
    email AS "Correo Electrónico",
    creado_en AS "Fecha Registro"
FROM usuarios;

-- Alias para tablas
SELECT u.username, u.email, p.estado
FROM usuarios AS u
INNER JOIN perfiles_voz AS p ON u.id = p.usuario_id;

-- El AS es opcional
SELECT u.username, u.email, p.estado
FROM usuarios u
INNER JOIN perfiles_voz p ON u.id = p.usuario_id;

1.4 SELECT con DISTINCT

-- Países únicos de usuarios
SELECT DISTINCT pais FROM usuarios WHERE pais IS NOT NULL;

-- Canales únicos usados en sesiones
SELECT DISTINCT canal FROM sesiones;

-- Modalidades únicas de mensajes
SELECT DISTINCT modalidad FROM mensajes;

-- Módulos únicos en logs
SELECT DISTINCT modulo FROM logs_ejecucion;

-- Combinaciones únicas de canal y usuario
SELECT DISTINCT canal, usuario_id FROM sesiones;

1.5 SELECT con LIMIT y OFFSET

-- Mostrar primeros 3 usuarios
SELECT * FROM usuarios LIMIT 3;

-- Mostrar 5 mensajes
SELECT * FROM mensajes LIMIT 5;

-- Paginación: saltar 2 y mostrar 3
SELECT * FROM usuarios ORDER BY id LIMIT 3 OFFSET 2;

-- Mostrar los 5 logs más recientes
SELECT * FROM logs_ejecucion 
ORDER BY ejecutado_en DESC 
LIMIT 5;

📝 Paso 2: SELECT con WHERE (Filtrado)

2.1 Operadores de Comparación

-- Usuarios de Perú
SELECT username, email, pais 
FROM usuarios 
WHERE pais = 'Perú';

-- Usuarios que NO son de Perú
SELECT username, email, pais 
FROM usuarios 
WHERE pais != 'Perú';

-- Mensajes con ID mayor a 10
SELECT id, contenido FROM mensajes WHERE id > 10;

-- Mensajes con ID menor o igual a 5
SELECT id, contenido FROM mensajes WHERE id <= 5;

-- Sesiones que expiran después del 2024-01-16
SELECT id, usuario_id, expira_en 
FROM sesiones 
WHERE expira_en > '2024-01-16';

2.2 BETWEEN (Rangos)

-- Usuarios con ID entre 3 y 7
SELECT id, username FROM usuarios WHERE id BETWEEN 3 AND 7;

-- Mensajes enviados entre 2024-01-15 y 2024-01-16
SELECT id, contenido, enviado_en 
FROM mensajes 
WHERE enviado_en BETWEEN '2024-01-15' AND '2024-01-16';

-- Sesiones con expiración en enero 2024
SELECT id, usuario_id, expira_en 
FROM sesiones 
WHERE expira_en BETWEEN '2024-01-01' AND '2024-01-31';

2.3 IN (Múltiples valores)

-- Usuarios de Perú, México y Argentina
SELECT username, pais 
FROM usuarios 
WHERE pais IN ('Perú', 'México', 'Argentina');

-- Sesiones en canales específicos
SELECT id, usuario_id, canal 
FROM sesiones 
WHERE canal IN ('telegram', 'web');

-- Mensajes de modalidad audio o video
SELECT id, contenido, modalidad 
FROM mensajes 
WHERE modalidad IN ('audio', 'video');

-- NOT IN (excluir valores)
SELECT username, pais 
FROM usuarios 
WHERE pais NOT IN ('Perú', 'México');

2.4 LIKE (Búsqueda de patrones)

-- Usuarios con email que termina en .com
SELECT username, email FROM usuarios WHERE email LIKE '%.com';

-- Usuarios con email de Gmail
SELECT username, email FROM usuarios WHERE email LIKE '%@gmail.com';

-- Usuarios cuyo username empieza con 'c'
SELECT username FROM usuarios WHERE username LIKE 'c%';

-- Usuarios cuyo username termina con 'z'
SELECT username FROM usuarios WHERE username LIKE '%z';

-- Usuarios cuyo username contiene 'ar'
SELECT username FROM usuarios WHERE username LIKE '%ar%';

-- Mensajes que contienen 'clima' (insensible a mayúsculas)
SELECT contenido FROM mensajes WHERE contenido ILIKE '%clima%';

2.5 IS NULL / IS NOT NULL

-- Usuarios con país registrado
SELECT username, pais FROM usuarios WHERE pais IS NOT NULL;

-- Usuarios sin país
SELECT username, pais FROM usuarios WHERE pais IS NULL;

-- Logs sin error
SELECT id, modulo, error FROM logs_ejecucion WHERE error IS NULL;

-- Logs con error
SELECT id, modulo, error FROM logs_ejecucion WHERE error IS NOT NULL;

2.6 Operadores Lógicos (AND, OR, NOT)

-- Usuarios de Perú que se registraron después del 2024-01-17
SELECT username, pais, creado_en 
FROM usuarios 
WHERE pais = 'Perú' AND creado_en > '2024-01-17';

-- Usuarios de Perú O México
SELECT username, pais 
FROM usuarios 
WHERE pais = 'Perú' OR pais = 'México';

-- Mensajes que NO son de texto
SELECT id, contenido, modalidad 
FROM mensajes 
WHERE modalidad != 'texto';

-- Combinación compleja
SELECT * FROM logs_ejecucion 
WHERE (modulo = 'clasificacion_nlp' OR modulo = 'api_clima')
AND exito = TRUE
AND tiempo_ejecucion_ms > 50;

📊 Paso 3: SELECT con ORDER BY

3.1 Ordenamiento Básico

-- Usuarios ordenados por username ascendente
SELECT username, email FROM usuarios ORDER BY username;

-- Usuarios ordenados por email descendente
SELECT username, email FROM usuarios ORDER BY email DESC;

-- Mensajes ordenados por fecha de envío (más recientes primero)
SELECT id, contenido, enviado_en 
FROM mensajes 
ORDER BY enviado_en DESC;

3.2 Ordenamiento Múltiple

-- Usuarios ordenados por país y luego por username
SELECT username, pais FROM usuarios 
ORDER BY pais ASC, username ASC;

-- Logs ordenados por módulo y luego por tiempo de ejecución
SELECT modulo, tiempo_ejecucion_ms, exito 
FROM logs_ejecucion 
ORDER BY modulo, tiempo_ejecucion_ms DESC;

-- Mensajes ordenados por remitente y fecha
SELECT remitente_id, contenido, enviado_en 
FROM mensajes 
ORDER BY remitente_id, enviado_en DESC;

3.3 Ordenamiento con NULLS

-- Pais con NULL al final
SELECT username, pais FROM usuarios 
ORDER BY pais NULLS LAST;

-- País con NULL al principio
SELECT username, pais FROM usuarios 
ORDER BY pais NULLS FIRST;

✏️ Paso 4: INSERT - Inserción de Datos

4.3 INSERT desde SELECT

-- Crear tabla de respaldo (primero la creamos si no existe)
CREATE TABLE IF NOT EXISTS usuarios_backup AS 
SELECT * FROM usuarios WHERE 1=0;  -- Solo estructura

-- Insertar usuarios de Perú en tabla de respaldo
INSERT INTO usuarios_backup (id, username, email, creado_en, pais, ciudad)
SELECT id, username, email, creado_en, pais, ciudad
FROM usuarios 
WHERE pais = 'Perú';

-- Verificar
SELECT * FROM usuarios_backup;

🔄 Paso 5: UPDATE - Actualización de Datos

5.1 UPDATE Básico

-- Actualizar país de un usuario
UPDATE usuarios 
SET pais = 'España' 
WHERE username = 'carlos_dev';

-- Verificar cambio
SELECT username, pais FROM usuarios WHERE username = 'carlos_dev';

-- Actualizar estado de perfil de voz
UPDATE perfiles_voz 
SET estado = 'completo', muestras_tomadas = 10 
WHERE usuario_id = 3;

-- Verificar
SELECT usuario_id, estado, muestras_tomadas 
FROM perfiles_voz 
WHERE usuario_id = 3;

5.2 UPDATE Múltiples Columnas

-- Actualizar varias columnas a la vez
UPDATE usuarios 
SET pais = 'México', ciudad = 'Cancún' 
WHERE username = 'maria_lopez';

-- Verificar
SELECT username, pais, ciudad 
FROM usuarios 
WHERE username = 'maria_lopez';

5.3 UPDATE con Expresiones

-- Incrementar muestras_tomadas en 1 para perfiles incompletos
UPDATE perfiles_voz 
SET muestras_tomadas = muestras_tomadas + 1 
WHERE estado = 'incompleto';

-- Verificar
SELECT usuario_id, muestras_tomadas, estado 
FROM perfiles_voz 
WHERE estado = 'incompleto';

-- Actualizar timestamp de última actividad
UPDATE sesiones 
SET ultima_actividad = NOW() 
WHERE usuario_id = 1;

🗑️ Paso 6: DELETE - Eliminación de Datos

6.1 DELETE Básico

-- Eliminar usuario test
DELETE FROM usuarios WHERE username = 'test1';

-- Verificar
SELECT * FROM usuarios WHERE username LIKE 'test%';

-- Eliminar sesiones de un usuario específico
DELETE FROM sesiones WHERE usuario_id = 11;

-- Verificar
SELECT * FROM sesiones WHERE usuario_id = 11;

6.2 DELETE con Condiciones

-- Eliminar logs con error
DELETE FROM logs_ejecucion WHERE exito = FALSE;

-- Verificar
SELECT * FROM logs_ejecucion WHERE exito = FALSE;

-- Eliminar mensajes antiguos (antes de enero 2024)
DELETE FROM mensajes WHERE enviado_en < '2024-01-01';

6.5 CUIDADO: DELETE sin WHERE

-- ¡PELIGRO! Esto elimina todos los registros
-- DELETE FROM logs_ejecucion;

-- Mejor usar TRUNCATE si se quiere vaciar toda la tabla
TRUNCATE TABLE logs_ejecucion;