Gestión de Bases de Datos
Guía de estudio + examen de simulación — Unidades 5 y 6 — ASIR
¿Para qué sirve? La sentencia INSERT INTO permite añadir filas nuevas a una tabla de la base de datos. Es la única forma de meter información que no existe aún.
Si la omites, debes dar valor a todas las columnas en el orden exacto en que están definidas en la tabla.
Cada valor se asigna por posición a la columna correspondiente. Los textos van entre comillas simples 'texto'.
INSERT INTO nombre_tabla [(col1, col2, col3)]
VALUES (valor1, valor2, valor3);
-- Inserción especificando columnas (recomendado)
INSERT INTO departments (department_id, department_name, manager_id, location_id)
VALUES (70, 'Public Relations', 100, 1700);
-- Inserción con SYSDATE (fecha actual del sistema)
INSERT INTO employees (employee_id, first_name, hire_date)
VALUES (300, 'Ana', SYSDATE);
-- Inserción con NULL explícito (el campo no tendrá valor)
INSERT INTO departments (department_id, department_name, manager_id)
VALUES (80, 'Logistica', NULL);
-- INSERT masivo: copiar múltiples filas de otra tabla de una sola vez
INSERT INTO clientes_respaldo (cliente_id, nombre, email, fecha_alta)
SELECT cliente_id, nombre, email, SYSDATE
FROM clientes
WHERE estado = 'ACTIVO';
-- Confirmar siempre los cambios
COMMIT;
Las columnas del SELECT deben coincidir en cantidad y tipo de dato con las del INSERT. Si insertas un texto donde espera un número, fallará.
SYSDATE es una función especial de Oracle que devuelve la fecha y hora actuales del servidor. Úsala siempre que necesites registrar cuándo ocurrió algo.
¿Para qué sirve? UPDATE cambia el contenido de filas que ya existen en la tabla. Si necesitas corregir un dato, cambiar un estado o actualizar un precio, usas UPDATE.
UPDATE nombre_tabla
SET columna1 = valor1 [, columna2 = valor2, ...]
[WHERE condicion];
Peligro crítico: si ejecutas UPDATE employees SET salary = 5000; sin WHERE, se actualiza el salario de TODOS los empleados a 5000. Siempre piensa en el WHERE antes de ejecutar.
-- Actualizar un solo empleado
UPDATE employees
SET salary = 8000
WHERE employee_id = 113;
-- Actualizar varias columnas a la vez
UPDATE employees
SET salary = salary * 1.10, -- subida del 10%
job_id = 'IT_PROG'
WHERE department_id = 60;
-- UPDATE con subconsulta: poner al empleado 114 el mismo salario que el 205
UPDATE employees
SET salary = (SELECT salary FROM employees WHERE employee_id = 205)
WHERE employee_id = 114;
COMMIT;
La subconsulta en el SET permite asignar un valor que viene de otro registro. La subconsulta debe devolver exactamente un valor (una fila, una columna).
¿Para qué sirve? DELETE elimina filas de una tabla. La estructura de la tabla (sus columnas y definición) queda intacta. Solo desaparecen los registros que cumplan la condición.
DELETE [FROM] nombre_tabla
[WHERE condicion];
-- El FROM es opcional en Oracle. Los dos son equivalentes:
DELETE FROM employees WHERE employee_id = 180;
DELETE employees WHERE employee_id = 180;
Peligro crítico: DELETE FROM employees; sin WHERE borra TODOS los registros de la tabla. La estructura queda, pero sin ningún dato.
- Es una sentencia DML
- Genera redo log (registro de cambios)
- Se puede deshacer con ROLLBACK
- Activa triggers si los hay
- Es una sentencia DDL
- Mucho más rápido que DELETE
- No se puede deshacer con ROLLBACK
- No activa triggers
-- Borrar todos los empleados del departamento que tenga 'Public' en el nombre
DELETE FROM employees
WHERE department_id = (
SELECT department_id
FROM departments
WHERE department_name LIKE '%Public%'
);
COMMIT;
¿Para qué sirve? Permite crear una tabla nueva y poblarla con datos en una sola operación, usando los resultados de un SELECT. Es la forma más eficiente de copiar o archivar datos en Oracle.
CREATE TABLE nombre_nueva_tabla AS
SELECT columnas
FROM tabla_origen
[WHERE condicion];
-- La clave: CREATE TABLE nombre AS y luego la consulta.
-- Lo que devuelva la consulta ES la nueva tabla (columnas + datos).
-- Copia completa: misma estructura y todos los datos
CREATE TABLE empleados_copia AS
SELECT * FROM employees;
-- Solo empleados del departamento 10
CREATE TABLE empleados_dept10 AS
SELECT * FROM employees
WHERE department_id = 10;
-- Tabla vacía con la misma estructura (WHERE imposible = 0 filas)
CREATE TABLE empleados_vacia AS
SELECT * FROM employees
WHERE 1 = 2;
-- Solo algunas columnas y con filtro de fecha
CREATE TABLE ventas_2026 AS
SELECT pedido_id, cliente_id, monto, fecha
FROM ventas
WHERE fecha >= '01-JAN-2026';
WHERE 1 = 2 es siempre falso, por lo que no se selecciona ninguna fila. La tabla se crea con las mismas columnas y tipos pero completamente vacía. Truco muy útil para crear una copia de estructura.
CTAS es DDL (como CREATE TABLE), por lo que no necesita COMMIT — el cambio es inmediato y definitivo. Tampoco se puede deshacer con ROLLBACK.
¿Qué es una transacción? Una transacción es una unidad lógica de trabajo que agrupa una o más operaciones (INSERT, UPDATE, DELETE) que se ejecutan como si fueran una sola. O se hacen todas, o no se hace ninguna.
Propiedades ACID
| Letra | Nombre | ¿Qué garantiza? | Ejemplo práctico |
|---|---|---|---|
| A | Atomicidad | Todo o nada. Si falla una operación, se deshacen todas. | Una transferencia bancaria descuenta y abona a la vez. Si falla el abono, también se deshace el descuento. |
| C | Consistencia | La BD pasa de un estado válido a otro. Se respetan todas las restricciones (NOT NULL, FK, etc.). | No puedes insertar un empleado en un departamento que no existe. |
| I | Aislamiento | Una transacción no ve los cambios intermedios de otra. Hasta el COMMIT, solo tú ves tus cambios. | Si borras un registro, los demás usuarios siguen viéndolo hasta que hagas COMMIT. |
| D | Durabilidad | Los cambios confirmados con COMMIT sobreviven a fallos del sistema. | Si se va la luz justo después del COMMIT, los datos se recuperan al reiniciar. |
Estados de una transacción
| Estado | ¿Cuándo ocurre? |
|---|---|
| Activa | Mientras se están ejecutando las operaciones normalmente. |
| Parcialmente ejecutada | La última operación terminó, pero aún no hay COMMIT ni ROLLBACK. Los cambios no son definitivos. |
| Ejecutada | Se ha hecho COMMIT. Los cambios son permanentes y visibles para todos. |
| Parcialmente abortada | Ha ocurrido un error en mitad de la ejecución. |
| Abortada | Se ha hecho ROLLBACK. La BD vuelve exactamente al estado anterior. |
COMMIT y ROLLBACK
-- COMMIT: hace los cambios PERMANENTES y visibles para todos
COMMIT;
-- ROLLBACK: DESHACE todos los cambios desde el último COMMIT
ROLLBACK;
-- Flujo típico
INSERT INTO employees ... ; -- cambio pendiente
UPDATE employees ... ; -- cambio pendiente
DELETE FROM employees ... ; -- cambio pendiente
-- En este punto, SOLO TÚ ves los cambios
COMMIT; -- ahora son permanentes y visibles para todos
-- o bien:
ROLLBACK; -- como si nunca hubiera pasado nada
Regla de oro: cualquier INSERT, UPDATE o DELETE necesita un COMMIT para ser permanente. Sin COMMIT, si cierras la sesión o se cae la conexión, los cambios se pierden automáticamente (ROLLBACK implícito).
¿Qué es PL/SQL? Es la extensión procedimental de SQL en Oracle. Permite combinar sentencias SQL con estructuras de programación (variables, condiciones, bucles). Un bloque anónimo es un programa sin nombre que no queda guardado en la base de datos.
[DECLARE
-- aquí se declaran variables y constantes (OPCIONAL)
]
BEGIN
-- aquí van las instrucciones que se ejecutan (OBLIGATORIO)
[EXCEPTION
-- aquí se gestionan los errores (OPCIONAL)
]
END;
/ -- la barra / ejecuta el bloque en SQL*Plus/Developer
Solo si necesitas variables. Se declaran aquí, antes del BEGIN.
El cuerpo del programa. Sin esto, el bloque no existe.
Declaración de variables
DECLARE
-- Texto variable (máx 9 caracteres)
v_emp_job VARCHAR2(9);
-- Número: 9 dígitos en total, 2 de ellos decimales
v_sueldo NUMBER(9,2) := 0;
-- Fecha: inicializada con hoy + 7 días
v_fecha DATE := SYSDATE + 7;
-- Constante (no cambia nunca)
c_iva CONSTANT NUMBER(3,2) := 21.00;
-- Booleano (solo TRUE, FALSE o NULL)
v_activo BOOLEAN := TRUE;
-- %TYPE: mismo tipo que la columna salary de la tabla employees
-- Si el tipo de la columna cambia, la variable se adapta sola
v_salario employees.salary%TYPE;
v_nombre employees.first_name%TYPE;
SELECT INTO — recuperar datos de la BD en variables
Dentro de PL/SQL, cuando quieres guardar el resultado de un SELECT en variables, debes usar INTO. La asociación es por posición: primera columna → primera variable, etc.
SELECT col1, col2, col3
INTO variable1, variable2, variable3
FROM tabla
WHERE condicion; -- debe devolver EXACTAMENTE 1 fila
-- Ejemplo completo:
DECLARE
v_fecha employees.hire_date%TYPE;
v_sueldo employees.salary%TYPE;
BEGIN
SELECT hire_date, salary
INTO v_fecha, v_sueldo
FROM employees
WHERE employee_id = 100;
DBMS_OUTPUT.PUT_LINE('Fecha: ' || v_fecha);
DBMS_OUTPUT.PUT_LINE('Salario: ' || v_sueldo);
END;
/
Si el SELECT devuelve más de una fila, se lanza la excepción TOO_MANY_ROWS. Si no devuelve ninguna, NO_DATA_FOUND. El SELECT INTO en PL/SQL siempre espera exactamente 1 fila.
Sentencia IF — ejecución condicional
La sentencia IF ejecuta unas instrucciones u otras según si una condición es verdadera o falsa.
IF condicion THEN
sentencias;
ELSIF otra_condicion THEN -- puede repetirse N veces
sentencias;
ELSE -- solo puede haber UNO, al final
sentencias;
END IF; -- siempre cierra con END IF
-- Ejemplo práctico:
DECLARE
v_edad NUMBER := 25;
BEGIN
IF v_edad < 18 THEN
DBMS_OUTPUT.PUT_LINE('Menor de edad');
ELSIF v_edad < 65 THEN
DBMS_OUTPUT.PUT_LINE('Adulto en activo');
ELSE
DBMS_OUTPUT.PUT_LINE('Jubilado');
END IF;
END;
/
ELSIF (sin espacio entre ELSE e IF): puede ponerse tantas veces como haga falta. ELSE: solo uno, siempre el último. IF no es un bucle, es una estructura condicional.
¿Para qué sirven los bucles? Para repetir un bloque de instrucciones un número determinado de veces o mientras se cumpla una condición.
| Característica | LOOP básico | WHILE | FOR |
|---|---|---|---|
| Evaluación condición | Al final (después de ejecutar) | Al principio (antes de ejecutar) | No aplica (usa contador) |
| ¿Ejecuta mínimo 1 vez? | Sí, siempre | No (puede ser 0 veces) | Sí, si el rango es válido |
| Declarar variable de control | Sí (en DECLARE) | Sí (en DECLARE) | No — el FOR la crea solo |
| Actualizar variable en el cuerpo | Sí, manualmente | Sí, manualmente | No — el FOR la actualiza solo |
| ¿Cuándo usarlo? | Cuando necesitas ejecutar al menos 1 vez | Cuando la condición puede ser falsa desde el inicio | Cuando sabes exactamente cuántas veces |
LOOP — bucle básico
LOOP
sentencia1;
sentencia2;
EXIT [WHEN condicion]; -- sin EXIT, el bucle es INFINITO
END LOOP;
-- Ejemplo: imprimir del 1 al 5
DECLARE
v_i NUMBER := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE('Valor: ' || v_i);
v_i := v_i + 1; -- actualización manual OBLIGATORIA
EXIT WHEN v_i > 5;
END LOOP;
END;
/
WHILE — bucle condicional
WHILE condicion LOOP
sentencias;
-- actualizar la variable de control aquí, si no: bucle infinito
END LOOP;
-- Ejemplo: imprimir del 1 al 5
DECLARE
v_i NUMBER := 1;
BEGIN
WHILE v_i <= 5 LOOP
DBMS_OUTPUT.PUT_LINE('Valor: ' || v_i);
v_i := v_i + 1; -- si olvidamos esto: bucle infinito
END LOOP;
END;
/
-- Caso especial: si v_i empieza en 10 y la condición es v_i <= 5,
-- el cuerpo del bucle NUNCA se ejecuta (0 iteraciones)
FOR — bucle contador (el más usado en el examen)
FOR variable_contador IN [REVERSE] limite_inferior..limite_superior LOOP
sentencias;
END LOOP;
-- NO declarar la variable. NO actualizarla. El FOR lo hace todo.
-- Ejemplo ascendente: imprime 1 2 3 4 5
BEGIN
FOR i IN 1..5 LOOP
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;
/
-- Ejemplo descendente con REVERSE: imprime 5 4 3 2 1
BEGIN
FOR i IN REVERSE 1..5 LOOP
DBMS_OUTPUT.PUT_LINE(i); -- imprime 5, 4, 3, 2, 1
END LOOP;
END;
/
-- Ejemplo con IF dentro del FOR: solo múltiplos de 3
BEGIN
FOR i IN 1..20 LOOP
IF MOD(i, 3) = 0 THEN
DBMS_OUTPUT.PUT_LINE(i || ' es múltiplo de 3');
END IF;
END LOOP;
END;
/
REVERSE: empieza en el límite superior y va bajando. FOR i IN REVERSE 1..5 imprime 5, 4, 3, 2, 1. Los límites se siguen escribiendo de menor a mayor.
En LOOP y WHILE, si olvidas actualizar la variable de control dentro del cuerpo, la condición nunca cambia y el bucle es infinito. En el FOR esto no puede pasar porque él mismo gestiona el contador.
¿Por qué hacer copias de seguridad? Los datos pueden perderse por fallos de hardware (cada pieza tiene un MTBF), desastres naturales o errores humanos. Las copias son la única garantía de recuperación.
Dimensión 1: temperatura (¿está el servidor activo?)
Se paran todos los servicios del gestor, se copian los ficheros de la BD y se vuelve a arrancar.
Ventaja: sencilla y garantiza consistencia total.
Desventaja: impide el acceso a los clientes durante la copia.
Se realiza con el servidor en marcha. Los usuarios siguen trabajando sin interrupciones.
Ventaja: no afecta al servicio.
Requisito: la BD debe estar en modo ARCHIVELOG.
Dimensión 2: naturaleza (¿qué se copia?)
Se copian los ficheros físicos de la BD (datafiles, redo logs, control files) tal cual están en el disco.
Ventaja: muy rápida.
Desventaja: no es portable entre distintos SGBD.
Se extraen los datos de las tablas exportándolos a ficheros de texto o CSV.
Ventaja: portable entre distintos SGBD (puedes importarla en MySQL, por ejemplo).
Desventaja: más lenta que la física.
Combinación imposible: la copia lógica en frío no existe. Si el servidor está parado, no se pueden hacer consultas SQL para extraer los datos.
Dimensión 3: estrategia (¿cuánto se copia?)
| Tipo | ¿Qué copia? | Ventaja | Desventaja | Frecuencia recomendada |
|---|---|---|---|---|
| Completa (full backup) | Todos los bloques usados de la BD, independientemente de si cambiaron | Restauración simple: solo necesitas un backup | Grande, lenta, impacto en rendimiento | 1 vez por semana |
| Incremental | Solo los bloques modificados desde el último backup | Rápida, ocupa poco espacio | Restauración compleja: debes aplicar la cadena completa | Diaria o cada pocas horas |
| Archive logs | Los ficheros redo log archivados | Permite recuperación hasta un instante exacto | Requiere modo ARCHIVELOG | Cada 15-60 minutos en producción |
INSERT INTO tabla [(cols)]
VALUES (vals);
INSERT INTO tabla [(cols)]
SELECT cols FROM origen
[WHERE cond];
COMMIT;UPDATE tabla
SET col = val [, col2 = v2]
[WHERE cond];
-- subconsulta en SET:
SET col = (SELECT val FROM t)
COMMIT;DELETE [FROM] tabla
[WHERE cond];
-- subconsulta en WHERE:
WHERE col = (SELECT ...)
COMMIT;CREATE TABLE nueva AS
SELECT cols FROM origen
[WHERE cond];
-- tabla vacía:
WHERE 1 = 2
-- es DDL → no necesita COMMITCOMMIT; -- hace permanente
ROLLBACK; -- deshace todo
-- Sin COMMIT, solo tú
-- ves los cambios[DECLARE vars]
BEGIN
SELECT c INTO v
FROM t WHERE cond;
DBMS_OUTPUT.PUT_LINE(x);
COMMIT;
[EXCEPTION WHEN e]
END;/IF cond THEN
sentencias;
ELSIF cond THEN -- N veces
sentencias;
ELSE -- max 1
sentencias;
END IF;LOOP
sentencias;
EXIT WHEN cond;
END LOOP;
-- Mínimo 1 ejecución
-- Variable: manualWHILE cond LOOP
sentencias;
v := v + 1; -- manual
END LOOP;
-- Puede ser 0 ejecuciones
-- Condición: al inicioFOR i IN [REVERSE] a..b
LOOP
sentencias;
END LOOP;
-- NO declarar i
-- NO actualizar iTemperatura: fría / caliente
Naturaleza: física / lógica
Estrategia:
completa (semanal)
incremental (diaria)
archive logs (15-60 min)
¡Lógica+fría = IMPOSIBLE!