MANUAL DE REPASO
SQL :: FLOTA ESPACIAL
Guia tactica para sustentacion. Modelo relacional, MySQL 8, programacion de servidor y patrones de consulta.
SISTEMA OPERATIVOTu base tiene 12 tablas. Antes de hablar de SQL, memoriza este mapa porque toda consulta sale de aqui:
FK = Foreign Key (apunta a la PK de otra tabla).
UK = Unique Key (no se puede repetir).
Las tablas en amarillo son tablas intermedias que resuelven relaciones muchos-a-muchos.
Cardinalidades en tu modelo
| Relacion | Cardinalidad | Como se ve en tablas |
|---|---|---|
| imperio ↔ flota | 1:N | FK imperio_codigo en flota |
| flota ↔ nave | 1:N | FK flota_codigo en nave |
| nave ↔ capitan | 1:1 | FK capitan_id en nave con UNIQUE |
| nave ↔ maniobra | N:M | Tabla intermedia nave_maniobra |
| flota ↔ mision | N:M | Tabla intermedia flota_mision |
| raza ↔ planeta | N:M con atributo | Tabla raza_planeta con porcentaje_poblacion |
| raza ↔ habilidad | 1:N | FK raza_nombre en habilidad (atributo multivaluado) |
| planeta ↔ montania | 1:N | FK planeta_cientifico en montania |
| Tipo | Cuando usarlo | Ejemplo en tu modelo |
|---|---|---|
INT | Numeros enteros normales (hasta 2.147 millones) | id_montania, id_mision |
BIGINT | Enteros muy grandes (billones) | poblacion_total (planetas tienen miles de millones) |
DECIMAL(p,s) | Numeros exactos con decimales. p=total, s=decimales | DECIMAL(5,2) para porcentaje (0-100 con 2 decimales) |
VARCHAR(n) | Texto variable, max n caracteres | VARCHAR(100) para nombres |
CHAR(n) | Texto FIJO de n caracteres | Codigos siempre del mismo largo |
TEXT | Texto muy largo (sin limite practico) | Descripciones extensas |
DATE / DATETIME | Fechas y fechas con hora | No usado en tu modelo pero es importante saber |
BOOLEAN | Verdadero/falso (en MySQL es TINYINT(1)) | Flags, activo/inactivo |
0.10, recuperas 0.10 exacto. Lo usamos para porcentajes, dinero, medidas precisas.FLOAT es aproximado (representacion binaria).
0.1 + 0.2 puede dar 0.30000000000000004. Por eso NO se usa para porcentajes o dinero.En tu modelo usamos DECIMAL para temperatura, velocidad, energia, altura y porcentaje. Si te preguntan por que: precision exacta sin errores de redondeo binario.
SELECT: la consulta basica
-- Todas las columnas, todas las filas SELECT * FROM planeta; -- Solo algunas columnas SELECT nombre_vulgar, poblacion_total FROM planeta; -- Con filtro WHERE SELECT * FROM planeta WHERE poblacion_total > 1000000000; -- Ordenado SELECT * FROM planeta ORDER BY poblacion_total DESC; -- Limitar resultados SELECT * FROM planeta ORDER BY poblacion_total DESC LIMIT 3;
Operadores en WHERE
| Operador | Uso | Ejemplo |
|---|---|---|
= | Igualdad | WHERE imperio_codigo = 'IMP-001' |
!= / <> | Distinto | WHERE nombre <> 'Tierra' |
> < >= <= | Comparacion | WHERE altura >= 5000 |
BETWEEN | Rango (inclusivo) | WHERE poblacion_total BETWEEN 1000 AND 5000 |
IN | Lista de valores | WHERE imperio_codigo IN ('IMP-001', 'IMP-002') |
LIKE | Patron de texto | WHERE nombre LIKE 'Imperio%' |
IS NULL | Valor nulo | WHERE nombre_vulgar IS NULL |
AND / OR / NOT | Combinacion logica | WHERE x = 1 AND y > 2 |
INSERT, UPDATE, DELETE
-- Insertar una fila INSERT INTO imperio (codigo_galactico, nombre, temperatura_promedio) VALUES ('IMP-007', 'Imperio Borg', 8.50); -- Insertar varias filas a la vez INSERT INTO mision (nombre) VALUES ('Reconocimiento'), ('Sabotaje'); -- Actualizar (siempre con WHERE, sino actualiza TODO) UPDATE planeta SET poblacion_total = 9000000000 WHERE nombre_cientifico = 'FM1073'; -- Borrar (siempre con WHERE) DELETE FROM capitan WHERE id_capitan = 'CAP-009';
Un JOIN combina filas de dos o mas tablas basandose en una condicion (generalmente la igualdad PK=FK).
| Tipo | Que devuelve | Cuando usarlo |
|---|---|---|
INNER JOIN | Solo las filas que coinciden en ambas tablas | Cuando solo te interesan registros con relacion completa |
LEFT JOIN | Todas las filas de la izquierda + las que coincidan de la derecha (NULL si no) | Cuando quieres ver "lo que esta solo en una tabla" |
RIGHT JOIN | Igual que LEFT pero al reves | Casi no se usa, mejor invertir las tablas y usar LEFT |
CROSS JOIN | Producto cartesiano (todas las combinaciones) | Casi nunca, generalmente es un error |
Ejemplos sobre tu modelo
-- INNER JOIN: planetas con su imperio SELECT p.nombre_vulgar, p.poblacion_total, i.nombre AS imperio FROM planeta p INNER JOIN imperio i ON i.codigo_galactico = p.imperio_codigo; -- LEFT JOIN: TODOS los capitanes, tengan o no nave SELECT c.nombre, n.codigo_nave FROM capitan c LEFT JOIN nave n ON n.capitan_id = c.id_capitan; -- LEFT JOIN con filtro IS NULL: capitanes SIN nave SELECT c.nombre FROM capitan c LEFT JOIN nave n ON n.capitan_id = c.id_capitan WHERE n.id_nave IS NULL; -- Multiples JOINs: nave, su flota, su capitan y su imperio SELECT n.codigo_nave, f.codigo_galactico AS flota, c.nombre AS capitan, i.nombre AS imperio FROM nave n INNER JOIN flota f ON f.codigo_galactico = n.flota_codigo INNER JOIN capitan c ON c.id_capitan = n.capitan_id INNER JOIN imperio i ON i.codigo_galactico = c.imperio_codigo; -- JOIN con tabla intermedia (N:M): misiones de cada flota SELECT f.codigo_galactico, m.nombre AS mision FROM flota f INNER JOIN flota_mision fm ON fm.flota_codigo = f.codigo_galactico INNER JOIN mision m ON m.id_mision = fm.mision_id;
2. Como se conectan? Busca el camino de PK-FK entre ellas.
3. Necesito todos o solo coincidencias? INNER si solo coincidencias, LEFT si quieres incluir "los huerfanos".
4. Usa alias cortos (
FROM planeta p) para escribir menos.
Las funciones de agregacion calculan un valor a partir de muchas filas:
| Funcion | Que hace |
|---|---|
COUNT(*) | Cuenta filas (incluso con NULL) |
COUNT(columna) | Cuenta filas con valor no nulo |
SUM(columna) | Suma valores numericos |
AVG(columna) | Promedio |
MIN(columna) | Valor minimo |
MAX(columna) | Valor maximo |
-- Total de planetas (lo que hace tu funcion total_planetas) SELECT COUNT(*) FROM planeta; -- Suma de poblacion de TODOS los planetas SELECT SUM(poblacion_total) FROM planeta; -- Planeta mas poblado SELECT MAX(poblacion_total) FROM planeta;
GROUP BY: agregar por grupos
Cuando quieres un agregado por categoria, usas GROUP BY:
-- Cuantos planetas tiene cada imperio SELECT imperio_codigo, COUNT(*) AS total_planetas FROM planeta GROUP BY imperio_codigo; -- Poblacion total por imperio SELECT imperio_codigo, SUM(poblacion_total) AS poblacion FROM planeta GROUP BY imperio_codigo; -- Promedio de energia de las naves por flota SELECT flota_codigo, AVG(energia_acumulada) AS energia_prom FROM nave GROUP BY flota_codigo;
HAVING: filtrar grupos
WHERE filtra filas individuales. HAVING filtra grupos despues de agregar.
-- Imperios con mas de 1 planeta SELECT imperio_codigo, COUNT(*) AS total FROM planeta GROUP BY imperio_codigo HAVING COUNT(*) > 1;
1. FROM → 2. WHERE → 3. GROUP BY → 4. HAVING → 5. SELECT → 6. ORDER BY → 7. LIMIT.
Por eso no puedes usar un alias del SELECT en el WHERE: cuando se ejecuta el WHERE, el SELECT todavia no paso.
-- Planetas con poblacion mayor al promedio SELECT nombre_vulgar, poblacion_total FROM planeta WHERE poblacion_total > ( SELECT AVG(poblacion_total) FROM planeta ); -- Capitanes que NO tienen nave (con NOT IN) SELECT nombre FROM capitan WHERE id_capitan NOT IN ( SELECT capitan_id FROM nave ); -- Imperios que tienen al menos un planeta (con EXISTS) SELECT nombre FROM imperio i WHERE EXISTS ( SELECT 1 FROM planeta p WHERE p.imperio_codigo = i.codigo_galactico );
- JOIN suele ser mas rapido y legible.
- Subconsulta es mas clara cuando es un calculo independiente (como un promedio que filtra).
- EXISTS es ideal para verificar existencia sin traer datos.
| FUNCTION | PROCEDURE | VIEW | |
|---|---|---|---|
| Devuelve | Un valor escalar | Nada o varios via OUT | Una tabla virtual |
| Se llama con | SELECT mifn() | CALL miproc() | SELECT * FROM mivista |
| Modifica datos? | No | Si | No (es un SELECT guardado) |
| Necesita DELIMITER | Si | Si | No |
| Tiene BEGIN/END | Si | Si | No |
| SQL dinamico (PREPARE) | No | Si | No |
Sintaxis de FUNCTION (lo que hiciste)
DELIMITER $$ CREATE FUNCTION total_planetas() RETURNS INT DETERMINISTIC BEGIN DECLARE total INT; SELECT COUNT(*) INTO total FROM planeta; RETURN total; END$$ DELIMITER ;
Que es DETERMINISTIC?
Le dice a MySQL que con los mismos parametros de entrada, siempre devolvera el mismo resultado. Es necesario en muchas configuraciones de MySQL (especialmente con replicacion activa).
Sintaxis de PROCEDURE (lo que hiciste)
DELIMITER $$ CREATE PROCEDURE insertar_imperio ( IN p_codigo VARCHAR(20), IN p_nombre VARCHAR(100), IN p_temperatura DECIMAL(6,2) ) BEGIN INSERT INTO imperio (codigo_galactico, nombre, temperatura_promedio) VALUES (p_codigo, p_nombre, p_temperatura); END$$ DELIMITER ;
Tipos de parametros
IN= entra al procedimiento (lo mas comun)OUT= sale del procedimiento (resultado)INOUT= entra y sale (poco usado)
Sintaxis de VIEW (lo que hiciste)
CREATE VIEW vista_planetas AS SELECT * FROM planeta ORDER BY poblacion_total ASC; -- Usar la vista SELECT * FROM vista_planetas;
- PROCEDURE: "haz esto". Ejecuta operaciones (incluyendo modificar datos).
- VIEW: "guardame esta consulta para no repetirla". Es como un SELECT con alias.
| Constraint | Que garantiza | Ejemplo en tu modelo |
|---|---|---|
PRIMARY KEY | Unicidad e identificacion de la fila | codigo_galactico en imperio |
FOREIGN KEY | Integridad referencial | imperio_codigo en flota |
UNIQUE | No se repiten valores | nombre_vulgar en planeta |
NOT NULL | Campo obligatorio | Casi todas las columnas |
CHECK | Validacion de valor | porcentaje_poblacion BETWEEN 0 AND 100 |
DEFAULT | Valor por defecto si no se especifica | Podria usarse en estados, fechas |
ON DELETE / ON UPDATE
Cuando defines una FK, decides que pasa si se borra o cambia la fila padre:
| Accion | Comportamiento | Cuando usarla |
|---|---|---|
RESTRICT | No deja borrar/actualizar si hay hijos | Entidades fuertes (imperio, planeta) |
CASCADE | Se propaga el cambio o borrado | Tablas intermedias N:M, atributos multivaluados |
SET NULL | Pone NULL en los hijos al borrar | Cuando la relacion es opcional |
NO ACTION | Similar a RESTRICT | Igual que RESTRICT en MySQL/InnoDB |
ON DELETE RESTRICT en relaciones a entidades fuertes (no borras un imperio si tiene planetas).-
ON DELETE CASCADE en tablas intermedias y atributos multivaluados (si borras una raza, sus habilidades se van con ella, porque no tienen sentido sin la raza).-
ON UPDATE CASCADE en todas, para que si cambia un codigo galactico se propague automaticamente.
Diferencia entre PRIMARY KEY y UNIQUE
- Solo puede haber UNA PRIMARY KEY por tabla. Puede haber varias UNIQUE.
- PRIMARY KEY no acepta NULL. UNIQUE si acepta NULL (uno por columna en MySQL).
- PRIMARY KEY es el identificador "oficial" de la fila.
Por que usaste DECIMAL en lugar de FLOAT?
0.1 + 0.2 = 0.30000000000000004). DECIMAL evita eso.
Por que existen las tablas intermedias?
flota_mision con las dos FKs como PK compuesta.
Por que la PK de nave es id_nave y no codigo_nave?
UNIQUE(flota_codigo, codigo_nave) para la regla de negocio.
Por que habilidad es una tabla aparte?
Que es la integridad referencial?
Diferencia entre DELETE y TRUNCATE
DELETE FROM t WHERE...: borra filas seleccionadas, se puede revertir con ROLLBACK en transaccion, dispara triggers.-
TRUNCATE TABLE t: borra TODAS las filas, mas rapido, no se puede revertir, resetea AUTO_INCREMENT, no dispara triggers.DELETE es DML, TRUNCATE es DDL.
Cuando usar INNER JOIN vs LEFT JOIN?
- LEFT JOIN: cuando quieres mostrar TODAS las filas de la tabla principal aunque no tengan relacion (con NULL del lado derecho).
Ejemplo: para mostrar capitanes con sus naves uso INNER si solo quiero los que tienen nave; uso LEFT si quiero incluir los que no tienen.
Que es DETERMINISTIC en una funcion?
Por que el DELIMITER se cambia a $$ ?
; normalmente para terminar instrucciones internas (como DECLARE, SELECT, INSERT). Si dejamos el delimitador en ;, Workbench interpretaria el primer ; interno como fin del CREATE. Cambiandolo a $$, le decimos "el fin real es $$" y los ; internos se respetan.
Que diferencia hay entre WHERE y HAVING?
- HAVING filtra grupos despues de agrupar (puede usar funciones de agregacion).
Ejemplo:
WHERE poblacion > 1000 filtra planetas individuales; HAVING COUNT(*) > 1 filtra grupos por cantidad de planetas.
Que pasa si borro un imperio con ON DELETE RESTRICT?
ERROR 1451: Cannot delete or update a parent row: a foreign key constraint fails porque hay flotas, planetas o capitanes que dependen de ese imperio. Para borrarlo, primero hay que borrar (o reasignar) todo lo que apunta a el.
Que es una transaccion?
START TRANSACTION, se confirma con COMMIT, o se deshace con ROLLBACK. InnoDB las soporta, MyISAM no.Propiedades ACID: Atomicidad, Consistencia, Aislamiento, Durabilidad.
Que es la normalizacion?
- 1FN: cada celda contiene un solo valor (atomico). No listas en una columna.
- 2FN: 1FN + cada atributo no clave depende de toda la PK (no parcial).
- 3FN: 2FN + no hay dependencias transitivas (un atributo no depende de otro atributo no clave).
Tu modelo cumple las tres.
Por que cada tabla existe (memoriza esto)
| Tabla | Justificacion |
|---|---|
imperio | Entidad principal con PK natural (codigo galactico unico) |
planeta | Entidad con PK natural (nombre cientifico unico) y FK al imperio |
montania | Atributo multivaluado de planeta (cantidad variable por planeta) |
capitan | Entidad con PK natural (identificacion unica) |
flota | Entidad con PK natural (codigo galactico unico) |
mision | Entidad para resolver atributo multivaluado de flota |
flota_mision | Tabla intermedia para relacion N:M flota-mision |
nave | Entidad con PK sustituta (porque su codigo solo es unico DENTRO de la flota) |
maniobra | Entidad con PK natural (el nombre la identifica) |
nave_maniobra | Tabla intermedia para relacion N:M nave-maniobra |
raza | Entidad con PK natural (nombre cientifico unico) |
habilidad | Atributo multivaluado de raza (varias frases por raza) |
raza_planeta | Tabla intermedia para N:M con atributo (porcentaje) |
Si te preguntan "que mejorarias?"
Respuesta lista para sonar sofisticado:
nombre_vulgar podrian beneficiarse).- Anadir auditoria: columnas
created_at, updated_at en cada tabla.- Usar triggers para validaciones complejas o logs automaticos.
- Implementar roles y permisos con GRANT, dando acceso solo a vistas en lugar de tablas base (importante para seguridad).
- Particionado de tablas grandes (no aplica con estos volumenes, pero es bueno mencionarlo).