Saltar a contenido

Modelo Físico: SQL - Instrucciones DML

Propuesta didáctica

Una vez creada la estructura de la base de datos mediante DDL, en esta segunda parte de la unidad vamos a iniciar el RA4 "Modifica la información almacenada en la base de datos utilizando asistentes, herramientas gráficas y el lenguaje de manipulación de datos".

Criterios de evaluación

Respecto al RA4:

  • CE4a: Se han identificado las herramientas y sentencias para modificar el contenido de la base de datos.
  • CE4b: Se han insertado, borrado y actualizado datos en las tablas.
  • CE4d: Se han diseñado guiones de sentencias para llevar a cabo tareas complejas.
  • CE4h: Se han adoptado medidas para mantener la integridad y consistencia de la información

Contenidos

Tratamiento de datos:

  • Inserción, borrado y modificación de registros.
  • Integridad referencial.

Cuestionario inicial

  1. ¿Cuál es el propósito de las sentencias DML?
  2. ¿Para qué sirve el comando INSERT INTO?
  3. ¿Qué sucede si omitimos el WHERE en un DELETE FROM?

Programación de Aula (7h)

Esta unidad es la quinta, con lo que se imparte en la primera evaluación, a finales del mes de noviembre. Tras una primera parte dedicada a DDL con seis sesiones lectivas, la segunda parte (DML) tiene una duración estimada de 7 sesiones lectivas:

Sesión Contenidos Actividades Criterios trabajados
7 DML. Inserciones AC511 CE4a, CE4b
8 DML. Actualizaciones y Borrados AC512 CE4b, CE4d, CE4h
9 Supuesto carga AC515 CE2b, CE2e, CE2h, CE4b, CE4d, CE4h
10 Supuesto DDL-DML I AC516 CE2a, CE2b, CE2c, CE2d, CE2e, CE4d
11 Supuesto DDL-DML II AC517 CE4b, CE4d, CE4h
12 Prueba objetiva PO518 RA2, RA4

Al finalizar esta unidad, comenzaremos un nuevo reto, centrado en el modelo físico, creando y cargando datos.

DML

Una vez que ya sabemos cómo crear las estructuras de datos para guardar la información, vamos a aprender a gestionarla.

CRUD

Las operaciones DML se asocian al acrónimo CRUD:

  • Create: insertar
  • Read: consultar
  • Update: modificar
  • Delete: eliminar

Sobre una tabla, las operaciones que vamos a poder realizar son:

  • insertar datos en una tabla
  • modificar datos de una tabla
  • eliminar datos de una tabla
  • consultar datos de una o más tablas.

Cuando hablamos de "datos de una tabla", podemos referirnos a registros completos, campos concretos o incluso elementos relacionados de varias tablas.

Para realizar estas operaciones, se utiliza el subconjunto DML (Data Manipulation Language) de SQL. En esta unidad nos vamos a centrar en la inserción, modificación y eliminación de datos, dejando las consultas para trabajarlas en profundidad en las siguientes unidades.

Para los siguientes ejemplos vamos a trabajar con la siguiente tabla:

CREATE TABLE dml8a ( 
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(64) NOT NULL DEFAULT 'Chuck Norris',
    email VARCHAR(64));

Inserciones

La sentencia para insertar datos es INSERT INTO, tanto en MariaDB como en PostgreSQL:

INSERT INTO tabla(col1, col2, ...) VALUES (valor1, valor2, ...);

A la hora de indicar la tabla, podemos omitir el nombre de las columnas, lo que implicará que deberemos indicar los valores para todas las columnas de la tabla. Si en cambio solo queremos insertar valores en algunas columnas, hemos de indicarlas de forma explícita al lado del nombre de la tabla.

Veamos unos ejemplos. Podemos insertar en la tabla dml8a utilizando:

INSERT INTO dml8a VALUES (1, 'Aitor Medrano', 'a.medrano@edu.gva.es');
INSERT INTO dml8a(id, nombre) VALUES (2, 'Pedro Casas');

Es decir, si no ponemos los nombres de los campos detrás del nombre de la tabla, en los valores deberemos introducir todos los valores siguiendo el orden de los campos según fueron creados (siempre puedes consultar la estructura de una tabla mediante DESCRIBE nombre_tabla). Además, los tipos de los datos deben concordar, incluso en el tamaño o precisión, debiendo haber una correspondencia posicional uno a uno entre las columnas y los valores introducidos.

Si queremos insertar varios registros con una única instrucción, los indicaremos separados por comas:

INSERT INTO dml8a VALUES (3, 'María Sánchez', 'maria@gmail.com'),
                         (4, 'Eva Amaral', 'eva@gmail.com');
-- Query OK, 2 rows affected (0.002 sec)
-- Records: 2  Duplicates: 0  Warnings: 0

Finalmente, si queremos insertar los valores por defecto asociados a una columna o un valor nulo (por ejemplo, a una clave ajena) usaremos DEFAULT y NULL respectivamente. Por ejemplo:

INSERT INTO dml8a VALUES (66, DEFAULT, NULL);
SELECT * FROM dml8a WHERE id=66;
-- +----+--------------+-------+
-- | id | nombre       | email |
-- +----+--------------+-------+
-- | 66 | Chuck Norris | NULL  |
-- +----+--------------+-------+
-- 1 row in set (0.003 sec)

Recuperando la clave subrogada

Cuando trabajamos con claves subrogadas, como es el caso de id que es un campo auto incrementable, podemos omitir su valor, y automáticamente se le asignará el siguiente valor de la secuencia:

INSERT INTO dml8a(nombre) VALUES ('José Manuel Pérez');

Cuando insertamos un registro en una tabla autoincrementable, podemos recuperar el valor creado mediante la función LAST_INSERT_ID(). En el bloque de programación mediante PL/SQL profundizaremos en su uso:

SELECT LAST_INSERT_ID();
-- +------------------+
-- | LAST_INSERT_ID() |
-- +------------------+
-- |               67 |
-- +------------------+
-- 1 row in set (0.001 sec)

¿Por qué el 67 y no el 5?

Aunque solo teníamos cinco registros, previamente habíamos insertado de forma explícita el id 66. El contador de un campo autoincrementable siempre continúa a partir del mayor valor existente, por lo que el siguiente valor generado es el 67. Los valores intermedios (del 5 al 65) no se reutilizan, del mismo modo que tampoco se reutilizan los identificadores de los registros que eliminemos.

En el caso de PostgreSQL, si queremos recuperar la información sobre la fila recién insertada, podemos emplear la cláusula RETURNING:

PostgreSQL
INSERT INTO dml8a (nombre, email)
    VALUES ('David Suárez', 'david@gmail.com')
    RETURNING id;

Si estamos trabajando con UUID como tipo de datos para las claves primarias a las cuales en el valor por defecto le asociamos un UUID, por ejemplo, mediante codigo UUID DEFAULT UUID(), para recuperar el valor insertado también utilizaremos INSERT ... RETURNING id:

INSERT ... RETURNING id
CREATE TABLE dml8a_UUID (
    codigo UUID DEFAULT UUID(),
    nombre VARCHAR(32),
    CONSTRAINT PK_dml8a_UUID PRIMARY KEY (codigo)
);

INSERT dml8a_UUID(nombre) VALUES ('Aitor') RETURNING codigo;
-- +--------------------------------------+
-- | codigo                               |
-- +--------------------------------------+
-- | 1e8e8786-cfaa-11f0-8ada-720fe3396247 |
-- +--------------------------------------+
-- 1 row in set (0.012 sec)

Usando variables

Otra opción que estudiaremos en el bloque de PL/SQL es el uso de variables:

SET @id = UUID();

INSERT INTO dml8a_UUID (codigo, nombre)
VALUES (@id, 'Aitor');

SELECT @id;
-- +--------------------------------------+
-- | @id                                  |
-- +--------------------------------------+
-- | 5a86a327-cfaa-11f0-8ada-720fe3396247 |
-- +--------------------------------------+
-- 1 row in set (0.001 sec)

Supuesto DML

A partir del Supuesto DDL, vamos a insertar los datos de las tablas para rellenar la base de datos (pero sin aplicar las modificaciones ALTER TABLE, trabajaremos directamente sobre el script de CREATE TABLE).

Para ello, es importante respetar las dependencias de las claves ajenas para primero insertar las tablas que no tienen dependencias. Por esta razón, primero insertaremos los datos en PROYECTO y luego en EMPLEADO, para posteriormente las tablas VEHICULO y ASIGNACION que dependen de las anteriores. Si no respetamos el orden, y por ejemplo, insertáramos los vehículos antes que los empleados, al hacerlo, obtendríamos diversos errores de integridad referencial.

Así pues, los comandos SQL necesarios son:

INSERT INTO PROYECTO (CODIGO, NOMBRE) VALUES
('101', 'GRIS'),
('102', 'BLANCO'),
('103', 'NEGRO'),
('104', 'VERDE');

INSERT INTO EMPLEADO (ID, NOMBRE, DNI, JEFE) VALUES
(10, 'ANA MARTIN', '1111A', NULL),
(15, 'JUAN LOPEZ', '2222B', 10),
(20, 'CARMEN DIAZ', '3333C', 10),
(25, 'ERNESTO GOMEZ', '4444D', 20),
(30, 'SILVIA GONZALEZ', '5555E', 15),
(35, 'FERNANDO SIERRA', '7777F', 15);

-- VEHICULO referencia a EMPLEADO
INSERT INTO VEHICULO (MATRICULA, MARCA, MODELO, PERSONA) VALUES
('2345AAA', 'Seat', 'Altea', NULL),
('1234BBB', 'Opel', 'Astra', 15),
('5555CCC', 'Seat', 'Ibiza', NULL),
('9876DDD', 'Lexus', 'LC', 10),
('1111FFF', 'Seat', 'Ibiza', NULL),
('2222GGG', 'Opel', 'Astra', 20);

-- ASIGNACION referencia tanto a EMPLEADO como a PROYECTO
INSERT INTO ASIGNACION (ID, CODIGO) VALUES
(10, '101'),
(15, '101'),
(20, '101'),
(10, '102'),
(15, '102'),
(30, '102'),
(35, '102'),
(20, '103');

¿Y si quiero insertar dos empleados, de manera que el primero sea el jefe del segundo, pero para ninguno de ellos quiero asignarle su código de manera explicita, ya que lo debe autogenerar el SGBD?

-- Insertamos el primer empleado
INSERT INTO EMPLEADO (NOMBRE, DNI, JEFE) VALUES ('Empleado Nuevo 1', '8888G', 10);
-- Al segundo empleado le asignamos como jefe el identificador de la última inserción
INSERT INTO EMPLEADO (NOMBRE, DNI, JEFE) VALUES ('Empleado Nuevo 2', '9999G', LAST_INSERT_ID());

-- Comprobamos los resultados
-- (recuperamos los empleados con un identificador superior al último que habíamos insertado)
select * from EMPLEADO where ID > 35;
-- +----+------------------+-------+------+
-- | ID | NOMBRE           | DNI   | JEFE |
-- +----+------------------+-------+------+
-- | 36 | Empleado Nuevo 1 | 8888G |   10 |
-- | 37 | Empleado Nuevo 2 | 9999G |   36 |
-- +----+------------------+-------+------+

Gestión de errores

Si insertamos datos con tipos diferentes, dejamos campos no nulos en blanco, o insertamos datos en un campo que es clave ajena y no cumplimos la integridad referencial, el SGBD nos devolverá diferentes mensajes:

INSERT INTO EMPLEADO VALUES ('cuarenta', 'Empleado con errores', '1234', NULL);
-- ERROR 1366 (22007): Incorrect integer value: 'cuarenta' for column `supuesto_ddl_proyecto`.`EMPLEADO`.`ID` at row 1

En cambio, si ejecutamos INSERT INTO EMPLEADO VALUES (40, 'JUAN PALOMO', 1234, NULL); no dará error porque convertirá el número 1234 a un string antes de insertarlo.

¿Y qué sucede si insertamos un dato que no cumple la integridad referencial en una clave ajena? Al intentar insertar un empleado que apunte a otro que no existe, obtendremos un error de fallo de la restricción de la clave ajena:

INSERT INTO EMPLEADO VALUES (45, 'Empleado con error en integridad referencial', '5678', 1);
-- ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails 
-- (`supuesto_ddl_proyecto`.`EMPLEADO`, CONSTRAINT `FK_EMPLEADO_JEFE` FOREIGN KEY (`JEFE`) REFERENCES `EMPLEADO` (`ID`) ON DELETE SET NULL ON UPDATE CASCADE)

También podemos insertar datos con el resultado de una consulta mediante la instrucción INSERT SELECT, pero esta operación la estudiaremos en profundidad más adelante.

Modificaciones

La sentencia para actualizar datos es UPDATE ... SET, tanto en MariaDB como en PostgreSQL:

UPDATE tabla SET campo=valor1 [,campo2=valor2];

Si queremos decidir qué elementos modificar, añadimos la expresión WHERE, ya que, si no, actualizaremos todos los registros de la tabla:

UPDATE tabla SET campo=valor WHERE condicion;

Veamos unos ejemplos. Podemos modificar el email de un usuario concreto:

UPDATE dml8a SET email='pedro@gmail.com' WHERE id=2;

O modificar varios campos de una sola vez:

UPDATE dml8a SET nombre='Pedro Casas García',
                 email='pedro.casas@gmail.com'
    WHERE id=2;

También podemos realizar cálculos al modificar un campo:

-- Incrementamos la población de Elche en un 10%
UPDATE CIUDAD SET poblacion = poblacion * 1.10 WHERE nombre = 'Elche';

El filtrado de los datos mediante la instrucción WHERE ofrece múltiples posibilidades, tanto con operadores relacionales, aritméticos, funciones de tratamiento de textos, fechas, etc. como comparación con el resultado de una consulta u operaciones de conjuntos. A partir de la próxima unidad profundizaremos en todas las alternativas del atributo WHERE.

Borrados

No te olvides...

Si no conoces este meme, hazte el favor y mira el siguiente vídeo.

Hice un delete...

La sentencia para borrar datos es DELETE FROM ..., tanto en MariaDB como en PostgreSQL:

DELETE FROM tabla;

Si queremos decidir qué elementos eliminar, añadimos la expresión WHERE, ya que si no, eliminaremos todos los registros de la tabla, lo cual es muy peligroso:

DELETE FROM tabla WHERE condicion;

Conviene destacar que, si queremos eliminar todos los datos de una tabla, es mucho más eficiente utilizar el comando TRUNCATE TABLE, tanto en MariaDB como en PostgreSQL:

TRUNCATE TABLE dml8a;

Por debajo, cuando truncamos una tabla, realmente está eliminando la tabla y volviéndola a crear, eliminando todos los ficheros de datos y reiniciando los campos autonuméricos.

Trabajando con fechas

Cuando vayamos a insertar o modificar un campo cuyo valor sea una fecha, dependiendo del tipo de dato y el formato que necesitemos, tenemos diferentes formas de hacerlo:

  • DATE: Solo fecha (YYYY-MM-DD)
  • DATETIME: Fecha y hora (YYYY-MM-DD HH:MM:SS)
  • TIMESTAMP: Similar a DATETIME pero con rango más limitado
  • TIME: Solo hora (HH:MM:SS)
  1. Formato estándar de cadena ISO (YYYY-MM-DD) es el más recomendado por su compatibilidad universal:

    -- INSERT
    INSERT INTO tabla (fecha) VALUES ('2025-12-03');
    INSERT INTO tabla (fecha_hora) VALUES ('2025-12-03 14:30:00');
    INSERT INTO tabla (hora) VALUES ('14:30:00');
    
    -- UPDATE
    UPDATE tabla SET fecha = '2025-12-04' WHERE id = 1;
    UPDATE tabla SET fecha_hora = '2025-12-04 16:45:00' WHERE id = 1;
    UPDATE tabla SET estado = 'vencido' WHERE fecha < '2025-01-01';
    UPDATE tabla SET estado = 'activo' 
    WHERE fecha BETWEEN '2025-01-01' AND '2025-12-31';
    
    -- DELETE
    DELETE FROM tabla WHERE fecha = '2025-12-03';
    DELETE FROM tabla WHERE fecha_hora = '2025-12-03 14:30:00';
    DELETE FROM tabla WHERE fecha BETWEEN '2025-01-01' AND '2025-03-31';
    DELETE FROM tabla WHERE fecha > '2025-12-31';
    
  2. Usando funciones de fecha, ideales para trabajar con fechas dinámicas y cálculos temporales:

    -- INSERT
    -- Fecha y hora actual
    INSERT INTO tabla (fecha_hora) VALUES (NOW());
    INSERT INTO tabla (fecha_hora) VALUES (CURRENT_TIMESTAMP());
    -- Solo fecha actual
    INSERT INTO tabla (fecha) VALUES (CURDATE());
    -- Solo hora actual
    INSERT INTO tabla (hora) VALUES (CURTIME());
    
    -- UPDATE
    -- Establecer fecha/hora actual
    UPDATE tabla SET fecha = NOW() WHERE id = 1;
    UPDATE tabla SET fecha = CURDATE() WHERE id = 1;
    -- Sumar o restar tiempo
    UPDATE tabla SET fecha = DATE_ADD(fecha, INTERVAL 7 DAY) WHERE id = 1;
    UPDATE tabla SET fecha = DATE_SUB(fecha, INTERVAL 1 MONTH) WHERE id = 1;
    UPDATE tabla SET fecha_hora = DATE_ADD(fecha_hora, INTERVAL 2 HOUR) WHERE id = 1;
    -- Actualizar según condiciones temporales
    UPDATE tabla SET estado = 'procesado' 
    WHERE MONTH(fecha) = MONTH(CURDATE()) 
    AND YEAR(fecha) = YEAR(CURDATE());
    
    -- DELETE
    -- Eliminar registros de hace más de 30 días
    DELETE FROM tabla WHERE fecha < DATE_SUB(CURDATE(), INTERVAL 30 DAY);
    -- Eliminar registros del año pasado
    DELETE FROM tabla WHERE YEAR(fecha) < YEAR(CURDATE());
    -- Eliminar registros más antiguos que 6 meses
    DELETE FROM tabla WHERE fecha < DATE_SUB(NOW(), INTERVAL 6 MONTH);
    -- Eliminar registros futuros
    DELETE FROM tabla WHERE fecha > CURDATE();
    
  3. Usando STR_TO_DATE() para formatos personalizados (por ejemplo, provenientes de formularios o archivos externos):

    -- INSERT
    INSERT INTO tabla (fecha) VALUES (STR_TO_DATE('03/12/2024', '%d/%m/%Y'));
    INSERT INTO tabla (fecha_hora) VALUES (STR_TO_DATE('12-03-2024 02:30 PM', '%m-%d-%Y %h:%i %p'));
    INSERT INTO tabla (fecha) VALUES (STR_TO_DATE('2024.12.03', '%Y.%m.%d'));
    
    -- UPDATE
    UPDATE tabla SET fecha = STR_TO_DATE('15/12/2024', '%d/%m/%Y') WHERE id = 1;
    UPDATE tabla SET fecha_hora = STR_TO_DATE('25-12-2024 10:30 AM', '%d-%m-%Y %h:%i %p') 
    WHERE id = 1;
    
    -- DELETE
    DELETE FROM tabla WHERE fecha = STR_TO_DATE('31/12/2023', '%d/%m/%Y');
    DELETE FROM tabla WHERE fecha < STR_TO_DATE('01/01/2024', '%d/%m/%Y');
    

    Los formatos empleados para STR_TO_DATE() son:

    • %d = Día (01-31)
    • %m = Mes (01-12)
    • %Y = Año con 4 dígitos (2024)
    • %y = Año con 2 dígitos (24)
    • %H = Hora formato 24h (00-23)
    • %h = Hora formato 12h (01-12)
    • %i = Minutos (00-59)
    • %s = Segundos (00-59)
    • %p = AM/PM

    Funciones de fecha

    Aunque las estudiaremos en detalle en la siguiente unidad, conviene conocer las siguientes funciones para manipulación de fechas:

    • DATE_ADD(fecha, INTERVAL valor unidad): Suma tiempo a una fecha
    • DATE_SUB(fecha, INTERVAL valor unidad): Resta tiempo a una fecha
    • DATEDIFF(fecha1, fecha2): Calcula diferencia en días entre fechas
    • TIMESTAMPDIFF(unidad, fecha1, fecha2): Permite especificar la unidad (YEAR, MONTH, DAY, HOUR, MINUTE, SECOND)

    Y para extracción:

    • YEAR(fecha), MONTH(fecha), DAY(fecha): Extraen componentes de la fecha
    • HOUR(hora), MINUTE(hora), SECOND(hora): Extraen componentes de la hora

En resumen, los formatos más comunes y recomendados son el estándar ISO (YYYY-MM-DD) y las funciones como NOW() o CURDATE() para fechas actuales.

Operaciones en cascada

Recuerda que, si eliminamos un registro referenciado por una clave ajena, se ejecutará la regla descrita en la tabla hija, de manera que es posible que no podamos eliminar ciertos datos al estar referenciados desde otra tabla (si estaban configuradas con NO ACTION o RESTRICT).

En cambio, si la clave ajena se configuró con CASCADE, al borrar el registro, se borrarán todos los registros de las tablas hijas que referenciaban a éste.

Finalmente, si se configuró con SET NULL, la clave ajena de la tabla hija se pondrá a nulo.

Para demostrar su uso, nos vamos a basar en el Supuesto DDL el cual define las siguientes tablas:

Supuesto DDL Proyecto

Si nos fijamos en la tabla ASIGNACION, tenemos que su definición de claves tanto hacia EMPLEADO como a PROYECTO es siempre en cascada:

CREATE TABLE ASIGNACION (
    ID INT NOT NULL,
    CODIGO VARCHAR(20) NOT NULL,

    CONSTRAINT PK_ASIGNACION PRIMARY KEY (ID, CODIGO),

    CONSTRAINT FK_ASIGNACION_EMPLEADO 
        FOREIGN KEY (ID) REFERENCES EMPLEADO(ID)
        ON DELETE CASCADE
        ON UPDATE CASCADE,

    CONSTRAINT FK_ASIGNACION_PROYECTO 
        FOREIGN KEY (CODIGO) REFERENCES PROYECTO(CODIGO)
        ON DELETE CASCADE
        ON UPDATE CASCADE
)

¿Piensas que es una buena decisión que los borrados se propaguen en cascada? Pues va a depender de cada caso. Si borramos un PROYECTO, se entiende que debería eliminar todas las asignaciones que tiene. Pero quizá sea una operación crítica la cual no queremos que sea tan fácil.

Si revisamos los datos, teníamos que el proyecto 104 no tiene ninguna asignación asociada, pero sí el 103. Al borrar el proyecto 103, con la definición de las claves ajenas que hemos definido, se borrarán todas las asignaciones a dicho proyecto.

delete from PROYECTO where CODIGO = 103;

select * from ASIGNACION;
-- +----+--------+
-- | ID | CODIGO |
-- +----+--------+
-- | 10 | 101    |
-- | 15 | 101    |
-- | 20 | 101    |
-- | 10 | 102    |
-- | 15 | 102    |
-- | 30 | 102    |
-- | 35 | 102    |
-- +----+--------+
-- 7 rows in set (0.001 sec)

A priori, parece una buena decisión, ya que si eliminamos un proyecto, tiene sentido liberar a los empleados de la asignación a dicho proyecto.

Veamos un ejemplo más problemático. Si nos centramos en la tabla EMPLEADO, tenemos que hay una relación reflexiva para expresar si un empleado es jefe de otro empleado, pero en este caso, la clave ajena pone a nulos los valores eliminados (en vez de propagar el borrado):

CREATE TABLE EMPLEADO (
    ID INT AUTO_INCREMENT,
    NOMBRE VARCHAR(100) NOT NULL,
    DNI VARCHAR(20) NOT NULL UNIQUE,
    JEFE INT,

    CONSTRAINT PK_EMPLEADO PRIMARY KEY (ID),

    CONSTRAINT FK_EMPLEADO_JEFE 
        FOREIGN KEY (JEFE) REFERENCES EMPLEADO(ID)
        ON DELETE SET NULL
        ON UPDATE CASCADE
);

Por ejemplo, el empleado 25 tiene configurado al empleado 20 como su jefe, y a su vez, este al 10.

select * from EMPLEADO where ID=25;
-- +----+---------------+-------+------+
-- | ID | NOMBRE        | DNI   | JEFE |
-- +----+---------------+-------+------+
-- | 25 | ERNESTO GOMEZ | 4444D |   20 |
-- +----+---------------+-------+------+

select * from EMPLEADO where ID=20;
-- +----+-------------+-------+------+
-- | ID | NOMBRE      | DNI   | JEFE |
-- +----+-------------+-------+------+
-- | 20 | CARMEN DIAZ | 3333C |   10 |
-- +----+-------------+-------+------+

¿Qué sucederá si eliminamos al empleado 20? Pues como la restricción de borrado está puesta a nulo, pondrá dicho valor en la clave ajena, indicando que aquellos empleados cuyo jefe era el empleado 20 se han quedado sin jefe, pero no se les ha eliminado a ellos.

delete from EMPLEADO where ID = 20;

select * from EMPLEADO where ID = 25;
-- +----+---------------+-------+------+
-- | ID | NOMBRE        | DNI   | JEFE |
-- +----+---------------+-------+------+
-- | 25 | ERNESTO GOMEZ | 4444D | NULL |
-- +----+---------------+-------+------+

Si eliminamos la clave ajena y la volvemos a crear para que haga el borrado en cascada, y a continuación volvemos a insertar los datos

ALTER TABLE EMPLEADO DROP CONSTRAINT FK_EMPLEADO_JEFE;
ALTER TABLE EMPLEADO ADD CONSTRAINT FK_EMPLEADO_JEFE
    FOREIGN KEY (JEFE) REFERENCES EMPLEADO(ID)
    ON DELETE CASCADE
    ON UPDATE CASCADE;

INSERT INTO EMPLEADO (ID, NOMBRE, DNI, JEFE) VALUES (20, 'CARMEN DIAZ', '3333C', 10);
UPDATE EMPLEADO SET JEFE = 20 WHERE ID = 25;

¿Qué sucederá con el empleado 25 al eliminar al empleado 20?

delete from EMPLEADO where ID = 20;

select * from EMPLEADO where ID = 25;
-- Empty set (0.001 sec)

Como el borrado es en cascada, al borrar el empleado 20 también se elimina el 25. Y si este fuera jefe de otro, se borrarían todos. Vamos a comprobarlo. Ahora en la tabla solo nos quedan cuatro empleados:

select * from EMPLEADO;
-- +----+-----------------+-------+------+
-- | ID | NOMBRE          | DNI   | JEFE |
-- +----+-----------------+-------+------+
-- | 10 | ANA MARTIN      | 1111A | NULL |
-- | 15 | JUAN LOPEZ      | 2222B |   10 |
-- | 30 | SILVIA GONZALEZ | 5555E |   15 |
-- | 35 | FERNANDO SIERRA | 7777F |   15 |
-- +----+-----------------+-------+------+

Si borramos el empleado 10, el cual es jefe del 15, el cual es jefe del 30 y del 35, ¡provocará un vaciado de todos los datos!

delete from EMPLEADO where ID=10;

select * from EMPLEADO;
-- Empty set (0.000 sec)

Fusionando datos

En ocasiones, necesitamos fusionar datos de una tabla origen con los datos de otra (destino), facilitando la sincronización de tablas o la integración de datos externos. Para ello, desde SQL:2003 podemos emplear la sentencia MERGE INTO, también llamada UPSERT en algunos motores, la cual permite combinar en una sola instrucción varias operaciones de manipulación de datos, basándose en una condición de coincidencia entre una tabla origen y una tabla destino, y realizando:

  • INSERT: cuando un registro no existe en la tabla destino.
  • UPDATE: cuando sí existe y queremos modificarlo.
  • DELETE (opcional) → cuando se cumple cierta condición adicional.

El problema es que MariaDB no la soporta. Mientras que PostgreSQL sí que tiene soporte completo para MERGE INTO, MariaDB ofrece la sentencia INSERT ON DUPLICATE KEY UPDATE que, aunque menos potente, permite obtener un comportamiento similar.

Su sintaxis es:

  • MariaDB

    En MariaDB usaremos INSERT ... ON DUPLICATE KEY UPDATE, ya sea mediante INSERT ... VALUES:

    INSERT INTO tabla_destino (col1, col2, ...)
    VALUES (valor1, valor2, ...)
    ON DUPLICATE KEY UPDATE
        col2 = valor2,
        col3 = valor3
        ...;
    

    O INSERT ... SELECT:

    INSERT INTO tabla_destino (col1, col2, ...)
    SELECT colA, colB, ... FROM tabla_origen
    ON DUPLICATE KEY UPDATE
        col2 = VALUES(col2),
        col3 = VALUES(col3)
        ...;
    

    En ambos casos, VALUES(columna) hace referencia al valor que se habría insertado en dicha columna de la tabla destino. En el caso de INSERT ... SELECT, también podemos referenciar directamente las columnas de la tabla origen (por ejemplo, col2 = tabla_origen.colB)

    Funciona cuando la tabla tiene una clave única (PRIMARY KEY o UNIQUE), de manera que si no existe, se inserta el nuevo registro, pero si existe (y colisiona en clave), entonces ejecuta el UPDATE.

  • PostgreSQL

    En PostgreSQL usaremos MERGE INTO:

    MERGE INTO tabla_destino AS destino
    USING tabla_origen AS origen
    ON (condición_de_coincidencia)
    WHEN MATCHED THEN
        UPDATE SET columna1 = valor1, columna2 = valor2
    WHEN NOT MATCHED THEN
        INSERT (columna1, columna2, ...)
        VALUES (valor1, valor2, ...);
    

    La sentencia MERGE INTO, además de poder añadir tantas cláusulas WHEN [NOT] MATCHED como necesitemos, permite añadir condiciones dentro para refinar mejor el comportamiento de la operación.

    Por ejemplo WHEN MATCHED and origen.precio > 0 THEN seleccionaría los registros que coincide la clave y cuyo precio es superior a 0.

Para empezar, vamos a realizar un ejemplo sencillo con MariaDB para entender su funcionamiento, insertando un nuevo empleado, y luego actualizando su información. Supongamos que tenemos los empleados conforme al Supuesto DML, los cuales tiene un ID autoincrementable y el DNI como clave única:

select * from EMPLEADO;
-- +----+-----------------+-------+------+
-- | ID | NOMBRE          | DNI   | JEFE |
-- +----+-----------------+-------+------+
-- | 10 | ANA MARTIN      | 1111A | NULL |
-- | 15 | JUAN LOPEZ      | 2222B |   10 |
-- | 20 | CARMEN DIAZ     | 3333C |   10 |
-- | 25 | ERNESTO GOMEZ   | 4444D |   20 |
-- | 30 | SILVIA GONZALEZ | 5555E |   15 |
-- | 35 | FERNANDO SIERRA | 7777F |   15 |
-- +----+-----------------+-------+------+
-- 6 rows in set (0.001 sec)

Si queremos insertar un nuevo empleado, pero con un DNI que ya existe, al ejecutar la siguiente sentencia obtendremos un error de clave duplicada, ya que el campo DNI es único:

insert into EMPLEADO (NOMBRE, DNI, JEFE) values ('Nuevo empleado', '3333C', 15);
-- ERROR 1062 (23000): Duplicate entry '3333C' for key 'DNI'

Sin embargo, si empleamos INSERT ... ON DUPLICATE KEY UPDATE, al intentar insertar el nuevo empleado, se detectará la colisión en el campo DNI y se actualizará el nombre y el jefe del empleado con dicho DNI:

insert into EMPLEADO (NOMBRE, DNI, JEFE) values ('Nuevo empleado', '3333C', 15)
ON DUPLICATE KEY UPDATE
    NOMBRE = VALUES(NOMBRE),
    JEFE = VALUES(JEFE);
-- Query OK, 2 rows affected (0.001 sec)

select * from EMPLEADO where DNI = '3333C';
-- +----+------------------+-------+------+
-- | ID | NOMBRE           | DNI   | JEFE |
-- +----+------------------+-------+------+
-- | 20 | Nuevo empleado   | 3333C |   15 |
-- +----+------------------+-------+------+

Filas afectadas en ON DUPLICATE KEY UPDATE

Al usar INSERT ... ON DUPLICATE KEY UPDATE, MariaDB devuelve un contador de filas afectadas que puede resultar confuso:

  • 1 fila afectada: se ha insertado un registro nuevo.
  • 2 filas afectadas: se ha actualizado un registro existente.
  • 0 filas afectadas: existía el registro, pero los valores ya eran los mismos.

A continuación, vamos a realizar un ejemplo más completo con ambos sistemas. Supongamos que tenemos una tabla con actualizaciones de empleados, los cuales debemos incorporar a nuestra tabla de EMPLEADO

CREATE TABLE ACTUALIZACIONES_EMPLEADO (
    ID INT AUTO_INCREMENT PRIMARY KEY,
    NOMBRE VARCHAR(100) NOT NULL,
    DNI VARCHAR(20) NOT NULL UNIQUE,
    JEFE INT);
  • MariaDB

    En MariaDB usaremos INSERT SELECT ... ON DUPLICATE KEY UPDATE:

    INSERT INTO EMPLEADO (NOMBRE, DNI, JEFE)
    SELECT NOMBRE, DNI, JEFE FROM ACTUALIZACIONES_EMPLEADO origen
    ON DUPLICATE KEY UPDATE 
        NOMBRE = VALUES(NOMBRE),
        JEFE = VALUES(JEFE);
    
  • PostgreSQL

    En PostgreSQL usaremos MERGE INTO indicando el campo por el cual va a comparar la clave (en este caso, el campo incrementable no nos sirve como clave identificadora entre diferentes empleados, y por lo tanto, empleamos el DNI):

    MERGE INTO EMPLEADO AS destino
    USING ACTUALIZACIONES_EMPLEADO AS origen
    ON destino.DNI = origen.DNI
    WHEN MATCHED THEN
        UPDATE SET NOMBRE = origen.NOMBRE,
            JEFE = origen.JEFE
    WHEN NOT MATCHED THEN
        INSERT (NOMBRE, DNI, JEFE)
        VALUES (origen.NOMBRE, origen.DNI, origen.JEFE);
    

Reto II - Creamos

En el reto anterior diseñamos una base de datos. En este reto nos vamos a centrar en la creación del modelo físico, creando y cargando datos, para posteriormente definir una serie de informes y KPIs que generaremos mediante consultas utilizando SQL.

KPI

Un KPI (key performance indicator) es una métrica cuantitativa que muestra un valor importante para el negocio, permitiendo comparar el desempeño de alguna característica.

Ejemplos de KPIs podrían ser la cantidad de pedidos realizados durante el último mes, beneficios obtenidos en el último año, comparación Year-over-year (YoY) de las ventas de este mes respecto a las ventas del mismo mes pero en el año pasado, etc.

Así pues, inicialmente crearemos la estructura de datos necesaria, la cargaremos con datos ficticios, y deberemos pensar qué información queremos extraer de ella.

Referencias

Actividades

  • AP510. (RABD.4 // CE4a // 3p) Haciendo uso de MariaDB y del cliente mariadb, a partir del siguiente script con las instrucciones para crear una base de datos, carga los datos que tienes disponibles en ap510.zip haciendo uso de las instrucciones LOAD DATA. Finalmente, mediante mariadb-dump realiza una exportación de toda la base de datos (con los datos cargados), y adjunta tanto los comandos empleados como el resultado de la exportación.

  • AC511. (RABD.4 // CE4a, CE4b // 3p) A partir de la actividad AC504, se pide:

    • Comprueba con SHOW CREATE TABLE qué juego de caracteres y colación tiene FABRICANTE y explica por qué se puede almacenar Kantō.
    • Inserta los siguientes datos utilizando las herramientas gráficas en MariaDB (y opcionalmente en PostgreSQL), comprobando que los datos se almacenan mostrando los caracteres especiales (fíjate en Kantō):

      • FABRICANTE

        codFabricante nombre provincia pais
        1 Sony Kantō Japón
        2 Microsoft Nuevo México USA
        3 Nintendo Kantō Japón
      • PIEZA

        codigo codFabricante* peso categoria precioVenta precioCompra existencias
        PS5 1 4.5 primera 500.00 550.00 123
        PS4 1 2.8 primera 300.00 400.00 234
        XSX 2 4.4 primera 600.00 550.00 345
        NSW 3 0.3 segunda 225.00 330.00 456
    • ¿Has podido insertar la pieza NSW? ¿Por qué?

    • Indica las sentencias SQL para vaciar las tablas.
    • Anota las sentencias SQL para introducir de nuevo los datos.
    • Añade en PIEZA una columna virtual tras precioCompra que se llame beneficio, y se obtenga a partir de la diferencia de las dos columnas de precio. Finalmente, ¿qué indica un beneficio negativo?

  • AC512. (RABD.4 // CE4b, CE4d, CE4h // 3p) Carga el siguiente script de datos sobre las tablas de la AC507, y realiza las siguientes modificaciones

    1. Añade un nuevo departamento llamado Formación.
    2. Renombra el departamento IT por Informática.
    3. Añade el campo email al final de la tabla DEPARTAMENTO, y rellénalos todos con email@ac512.com.
    4. Incrementa el salario de todos los empleados del departamento de informática en 300€.
    5. Añade un campo fecha_nac tras la columna nombre de la tabla EMPLEADO y rellénala con diversos valores.
    6. Añade una columna virtual que calcule la edad de cada empleado.
    7. Reduce el salario de Pedro Rivas un 10%.
    8. Inserta un nuevo empleado con tus datos en el departamento Formación.
    9. Traslada al empleado 12345678A al departamento de Formación.
    10. Modifica la clave del departamento Formación a un nuevo UUID.
    11. Elimina el departamento Legal. ¿Has podido? ¿Qué ha pasado con sus empleados?

  • AP513. (RABD.4 // CE4b, CE4h // 3p) Sobre la actividad AC507, añade una nueva tabla DIETAS donde podamos anotar los gastos ocasionados por las dietas de los empleados, como son la fecha, el coste y una descripción de la misma. A continuación, inserta 10 dietas de diferentes empleados.

    Debes tener en cuenta que si eliminamos un empleado, no debe permitir realizar la operación ni eliminar las dietas, ya que, si no, luego no nos cuadraría la contabilidad. ¿Cómo solucionamos dar de baja a un empleado pero no eliminarlo del sistema?

  • AR514. (RABD.4 // CE4b, CE4d // 3p) Carga el script ar514.sql con el DDL sobre una base de datos de libros y préstamos, y realiza las siguientes operaciones:

    1. Inserta los siguientes valores en las tablas:

      • LIBRO

        codigo titulo autor stock genero
        1 Historia de España J. Pérez 5 HIS
        2 Reina Roja J. Gómez-Jurado 33 NOV
      • PRESTAMO

        codLibro codSocio fentrega fdevolucion
        1 1 15/10/24
        2 1 25/10/24
    2. Realiza las siguientes operaciones:

      1. Cambia la clave primaria de libro al título. ¿Es buena idea?. Realiza los cambios necesarios para que la tabla que contiene los libros y prestamos estén relacionadas.
      2. Eliminar aquellos libros que sean del género de historia. ¿Se elimina el registro de la tabla LIBRO? Razona la respuesta.
        1. Si no lo has conseguido, cambia la restricción de la clave ajena para que la operación se propague.
        2. Vuelve a realizar la operación de borrado de libros.
        3. ¿Cuántos registros tiene ahora la tabla PRESTAMO?
      3. Inserta los siguientes libros (Loba Negra, MariaDB Essentials), buscando en Internet la información que necesites.
      4. Reduce el stock de Reina Roja en 3 unidades.
      5. Haz que el libro prestado tenga como fecha de devolución el 31/12/24.
      6. Cambia el nombre del autor de Reina Roja por Juan Gómez Jurado.

  • AC515. (RABD.2, RABD.4 // CE2b, CE2e, CE2h, CE4b, CE4d, CE4h // 3p + 3p) El gimnasio SeveroFit nos ha pasado dos scripts, uno para crear su base de datos (ac515.sql) y otro para cargar sus datos (ac515-datos.sql), pero quien los escribió no llegó a probarlos.

    Nos han pedido depurarlos hasta que funcionen y cumplan todos los requisitos del gimnasio:

    • Monitores: se identifican mediante un código autonumérico. El DNI y el nombre son obligatorios, y no puede haber dos monitores con el mismo DNI ni con el mismo email (que es opcional). Si no se indica la fecha de alta, será la fecha actual.
    • Socios: se identifican mediante un código autonumérico. El DNI y el nombre son obligatorios, y no puede haber dos socios con el mismo DNI. La cuota debe estar comprendida entre 20 y 200 € (35 € por defecto) y el tipo de abono puede ser mensual, trimestral o anual (por defecto, mensual).
    • Actividades: se identifican mediante un código de 4 caracteres. El nombre es obligatorio y la descripción, opcional. Cada actividad tiene entre 1 y 30 plazas y puede estar impartida por un monitor. Si se elimina un monitor, sus actividades se quedan sin monitor.
    • Inscripciones: un socio solo puede inscribirse una vez en cada actividad, y se guarda la fecha de inscripción (por defecto, la actual). Si un socio se da de baja, se eliminan sus inscripciones, pero no se puede eliminar una actividad que tenga socios inscritos.

    El cliente mariadb se detiene en el primer error e indica la línea en la que se produce, por lo que podrás ir corrigiéndolos de uno en uno. Por cada error, anota en una tabla: el número de línea, el código y mensaje de error, la causa y la corrección aplicada.

    Se pide:

    1. Ejecuta el script DDL y corrige sus errores, tanto de sintaxis como de semántica, hasta que se ejecute sin errores:

      docker exec -i mdb mariadb -u s8a -ps8a < ac515.sql
      
    2. Una vez se ejecute sin errores, revisa que cumple todos los requisitos: hay 3 errores más que no provocan ningún mensaje, pero que permiten guardar datos incorrectos. Para cada uno, escribe una sentencia que demuestre el problema, corrígelo y comprueba que, tras la corrección, el SGBD rechaza la operación.

    3. Conéctate con la herramienta gráfica que desees y adjunta una captura del modelo físico de la base de datos.

    4. A continuación, ejecuta el script de datos y corrige sus errores. Cuando un dato incumpla un requisito, corrige el dato, no la estructura:

      docker exec -i mdb mariadb -u s8a -ps8a < ac515-datos.sql
      

      El script contiene 10 errores. Si con tu base de datos aparecen menos, es que tu DDL todavía admite datos incorrectos: vuelve a la parte 1.

    5. Escribe las sentencias SQL para realizar las siguientes operaciones y responde a las preguntas:

      1. Reduce un 10% la cuota de los socios con abono anual.
      2. Amplía en 10 el número de plazas de todas las actividades. ¿Qué ha ocurrido? ¿Se ha modificado alguna actividad? ¿Por qué?
      3. Elimina la actividad de Yoga. ¿Has podido? ¿Por qué?
      4. La actividad de Spinning pasa a tener el código CICL. ¿Qué ha pasado con sus inscripciones?
      5. El monitor Marcos Gil deja el gimnasio. ¿Qué ha pasado con sus actividades?
      6. Ana López se da de baja como socia. ¿Cuántas inscripciones quedan?
      7. Ahora sí, elimina la actividad de Yoga. ¿Qué has tenido que hacer antes? ¿Cuántas actividades quedan?

    Debes entregar en Aules los dos scripts corregidos, la tabla de errores, las sentencias de prueba del apartado 1 con su resultado, la captura del diagrama y las sentencias y respuestas de la parte 4.

    Mismo error, distinta causa

    Fíjate bien en los mensajes: en el script de creación, dos de los errores producen exactamente el mismo mensaje, pero su causa es diferente.

  • AC516. (RABD.2 // CE2a, CE2b, CE2c, CE2d, CE2e, CE4d // 3p) A partir del siguiente modelo relacional, genera el script DDL (ac516.sql) para la creación de las tablas mediante MariaDB en una base de datos denominada ac516 (el script debe poder ejecutarse una y otra vez, de manera que si ya existe la base de datos debe borrarla, y para crear cada tabla, debe comprobar que no exista previamente).

    El diccionario de datos es el siguiente:

    TABLA CAMPO TIPO RESTRICCIONES
    ALUMNOS id Entero sin signo autoincrementable PK
    dni Carácter (9) UK
    nombre Texto (64) obligatorio
    apellidos Texto (64) obligatorio
    sexo {H, M} obligatorio
    direccion Texto (128)
    telefono Carácter (12)
    fnac Fecha
    ASIGNATURAS id Entero sin signo autoincrementable PK
    nombre Texto (64) obligatorio
    aula entero Entre 1 y 10, por defecto se asigna el aula 1
    duracion Entero Entre 1 y 12, por defecto su duración es 3
    NOTAS idAlumno Entero sin signo PK, FK → ALUMNOS
    idAsignatura Entero sin signo PK, FK → ASIGNATURAS
    fecha Fecha obligatorio
    calificacion Numérico con 1 decimal Entre 0.0 y 10.0, obligatorio

    A continuación, modifica la estructura de las tablas mediante ALTER TABLE:

    1. Cambia el nombre de las tablas y ponlas en singular.
    2. Añade una columna recuperacion a NOTA para almacenar la posible calificación de un examen de recuperación. Dicha calificación debe estar comprendida entre 0 y 10.
    3. Modifica la columna sexo de ALUMNO para añadir un tercer valor (indeterminado - I).
    4. Modifica la clave primaria de NOTA para incluir también la fecha.
    5. Modifica la clave ajena de NOTA hacia ALUMNO para que al realizar un borrado de un alumno, se eliminen todas sus notas.

  • AC517. (RABD.4 // CE4b, CE4d, CE4h // 3p) Continúa la actividad anterior, y crea el script ac517.sql donde insertes los siguientes datos para el alumnado y los cursos mediante instrucciones INSERT:

    • ALUMNO

      DNI Nombre Apellidos Sexo Dirección Teléfono Fecha de nacimiento
      12345678A Johnny Mentero H Debajo del puente, -1 NULL 25/12/80
      23456789B María Gracia M NULL 636112233 NULL
      34567890C Armando Casas I Villarriba, 33 636223344 31/12/90
      45678901D Mario Neta H Circo del Sol, 22 636334455 NULL
      56789012E Susana Oria M Calle La Granja NULL 3/1/00
    • ASIGNATURA

      id Nombre Aula Duración
      NULL SQL 3 9
      666 Humor NULL 12
      NULL Cocina 6 NULL

    Tras insertar los datos, contesta a las siguientes cuestiones:

    1. ¿Qué aula tiene Humor? ¿Por qué?
    2. ¿Qué duración tiene la asignatura de Cocina? ¿Por qué?
    3. ¿Se han cargado todas las calificaciones?

    A continuación, inserta los siguientes datos de calificaciones mediante LOAD DATA, creando previamente el archivo ac516.csv con la siguiente información:

    Alumno Asignatura Fecha Nota Recuperación
    1 1 20/12/20 7.2 NULL
    1 666 20/12/20 9.9 NULL
    2 667 10/1/21 5 NULL
    2 666 10/1/21 7 NULL
    3 1 10/1/21 3 6
    3 666 10/1/21 3 6

    Finalmente, realiza las siguientes operaciones de modificación y borrado de datos:

    1. Modificar:

      1. El teléfono del alumno 1 es 666111222
      2. La dirección de todos los alumnos hombres es Donde me lleve la vida.
      3. El apellido de todos alumnos que se llamen Armando ahora es Bullas.
      4. Todos los cursos tendrán una unidad más de duración
      5. Los alumnos que no tengan dirección se les asignará Internet.
    2. Eliminar:

      1. Borra las calificaciones inferiores a 4. ¿Has podido? ¿Cuántas calificaciones quedan?
      2. Borra la información de Johnny Mentero. ¿Has podido? ¿Cuántos alumnos quedan? ¿Cuántas calificaciones quedan?
      3. Elimina el curso de cocina ¿Has podido? ¿Cuántas asignaturas quedan?
      4. Elimina los alumnos que no tengan teléfono ¿Has podido? ¿Cuántos alumnos quedan? ¿Cuántas calificaciones quedan?
      5. Elimina todos los alumnos ¿Has podido? ¿Cuántos alumnos quedan? ¿Cuántas calificaciones quedan?

    Recuerda que para comprobar los datos que contiene una tabla puedes ejecutar en el cliente SELECT * FROM tabla;

  • PO518. (RABD.2, RABD.4 // todos los CE // 40p + 20p) La prueba objetiva agrupa los criterios de evaluación asociados al DDL (RABD.2) y al DML (RABD.4), consistiendo en:

    • Crear y/o modificar una estructura de una base de datos.
    • Insertar/modificar/eliminar datos de una base de datos.

  • PY519. (RABD.2, RABD.4 // CE2a, CE2b, CE2c, CE2d, CE2e, CE4a, CE4b, CE4d // 5p + 5p) A partir del modelo diseñado en la actividad PY417 asociada al primer reto de diseño de la base de datos, ahora nos vamos a centrar en crear el modelo físico y cargar datos para trabajar con él. Para ello, como equipo, deberéis decantaros por un SGBD en concreto.

    Para ello, se pide:

    • Informe con el modelo ER, el modelo MR y justificación de las decisiones tomadas respecto a las restricciones incluidas en el modelo físico.
    • Script SQL con las sentencias DDL.
    • Script SQL con las sentencias DML para cargar datos.
    • Script SQL para vaciar todas las tablas, teniendo en cuenta las claves ajenas y el orden de borrado.

    Se utilizará una rúbrica para su evaluación en base a la siguiente lista de cotejo:

    • Limpieza y calidad de los scripts.
    • Documentación de los scripts.
    • El informe entregado no contiene faltas de ortografía.
    • El informe entregado tiene un formato adecuado (portada, apartados, autores, etc.).
    • El informe debe indicar cómo se han repartido las tareas y qué ha realizado cada alumno/a.
    • Cada tabla contiene un mínimo de 10 registros, salvo justificación.
    • En el caso de existir relaciones entre tablas, cada registro debe relacionarse con un mínimo de tres registros más.

  • AR520. (RABD.2, RABD.4 // CE2a, CE2b, CE2c, CE2d, CE2e, CE4a, CE4b, CE4d // 3p) Una vez finalizada la unidad, responde todas las preguntas del cuestionario inicial, con al menos un par de líneas para cada una de las cuestiones.