Skip to main content

Integridad en bases de datos relacionales

Cuando diseñas una base de datos, no basta con crear tablas y columnas. Lo más importante es que los datos que guardes representen correctamente la realidad. Si eso falla, cualquier aplicación que dependa de ellos también fallará, sin importar lo bien que esté programada. Por eso existen las reglas de integridad: un conjunto de principios que aseguran que la información sea coherente, completa y válida.

Podemos entender la integridad como la base que sostiene cualquier sistema relacional. Sin ella, surgen duplicidades, datos contradictorios, referencias rotas o valores imposibles. Todas las bases de datos relacionales, incluido SQLite, se basan en cuatro tipos principales de integridad. Vamos a verlos con calma y con ejemplos sencillos.

Integridad de entidad

Esta regla garantiza que cada fila de una tabla sea única y pueda identificarse sin confusiones. Para conseguirlo, toda tabla debe tener una clave primaria. Esa clave actúa como el identificador lógico de cada registro.

De forma sencilla, la integridad de entidad asegura tres ideas fundamentales:

  • Cada fila representa exactamente una cosa del mundo real.
  • Ninguna fila puede duplicar la identidad de otra.
  • La clave primaria nunca puede ser nula.

Imagina una tabla de estudiantes donde dos alumnos comparten el mismo id. Para el sistema serían la misma persona, aunque tengan nombres distintos. Esto haría imposible saber a quién pertenece información relacionada, como notas o matrículas. Por eso las claves primarias son obligatorias: sin ellas, una base de datos pierde la capacidad de diferenciar sus propios datos.

La Importancia de la Clave Primaria

La clave primaria es un campo (o conjunto de campos) en una tabla de una base de datos que identifica de forma única cada fila (registro) de esa tabla. Debe ser única y no puede contener valores nulos.

Escenario sin Clave Primaria Única

Imagina la tabla de estudiantes que mencionas, donde el campo ID se usa para identificar a los alumnos, pero dos estudiantes diferentes (con nombres distintos) tienen el mismo ID.

IDNombreApellido
101JuanPérez
102MaríaLópez
101AnaGarcía

El Problema:

  • El sistema trata las filas con el mismo ID (101) como si fueran la misma entidad.
  • Si intentas registrar una nota o una matrícula para el ID 101, el sistema no puede saber si esa información pertenece a Juan Pérez o a Ana García.
  • Se produce una pérdida de integridad e imposibilidad de diferenciar los datos, haciendo la base de datos inútil para la gestión precisa de la información.

Escenario con Clave Primaria Única

Si cada estudiante tiene un ID único que actúa como Clave Primaria, el sistema puede distinguirlos sin problemas.

ID (Clave Primaria)NombreApellido
101JuanPérez
102MaríaLópez
103AnaGarcía

La Solución:

  • Cada registro está identificado de forma única.
  • La información de notas o matrículas siempre se puede vincular de forma inequívoca al estudiante correcto (por ejemplo, al ID 103 para Ana García).
  • La clave primaria garantiza la integridad y la coherencia de los datos, que es fundamental para cualquier sistema de bases de datos.

Integridad referencial

La integridad referencial evita incoherencias entre tablas relacionadas. Se aplica cuando una tabla depende de otra a través de una clave foránea. El principio es simple: una clave foránea siempre debe apuntar a un registro que exista realmente.

Este tipo de integridad garantiza que:

  • No existan pedidos que apunten a un cliente inexistente.
  • No existan matrículas que hagan referencia a cursos que no están registrados.
  • No existan préstamos asociados a ejemplares que nunca se dieron de alta.

Puedes imaginarlo como un conjunto de vínculos seguros entre las tablas. Si uno de ellos pudiera romperse, la información dejaría de ser fiable. Además, la integridad referencial define qué ocurre cuando un registro principal se modifica o se elimina. Estos comportamientos se controlan mediante reglas como ON DELETE RESTRICT, ON DELETE CASCADE u ON UPDATE SET NULL, que aprenderás cuando entremos en SQLite.

La Clave Foránea y la Integridad Referencial

Una Clave Foránea en una tabla (la tabla "hija") apunta a la Clave Primaria de otra tabla (la tabla "padre"). Esto garantiza que los datos en la tabla hija siempre hagan referencia a un registro existente y válido en la tabla padre.

Gráfico de Vínculos Seguros

Imaginemos dos tablas relacionadas: la tabla Estudiantes y la tabla Matrículas.

Tabla "Padre": EstudiantesTabla "Hija": Matrículas
ID_Estudiante (PK)ID_Estudiante (FK)
NombreVínculo SeguroCódigo_Clase
Apellido$\longleftarrow$Fecha_Matrícula
(Registros principales)(Referencias a la tabla Estudiantes)

El Vínculo Seguro (Integridad Referencial):

  1. La columna ID_Estudiante en la tabla Matrículas es la Clave Foránea (FK).
  2. Esta FK apunta y depende de la Clave Primaria (PK) ID_Estudiante en la tabla Estudiantes.
  3. Resultado: Nunca podría existir una matrícula para un ID_Estudiante que no exista en la tabla principal de Estudiantes.

¿Qué pasa si el vínculo se rompe?

Si se permitiera que un registro de matrícula existiera para un ID_Estudiante que ya no existe en la tabla de estudiantes, la información se volvería incoherente o no fiable (tendrías una matrícula "huérfana" que no pertenece a nadie).

Reglas de Control (ON DELETE/UPDATE)

La Integridad Referencial no solo establece el vínculo, sino que también define cómo la base de datos debe reaccionar cuando se intenta modificar o eliminar un registro en la tabla "padre" (Estudiantes).

ReglaComportamiento al Eliminar el Estudiante (Padre)Resultado en Matrículas (Hija)
ON DELETE RESTRICTRestringe/Impide la eliminación.El sistema impide borrar al estudiante si tiene matrículas asociadas. (Más seguro)
ON DELETE CASCADEPropaga la eliminación.El sistema borra automáticamente todas las matrículas asociadas a ese estudiante. (Borrado en cascada)
ON UPDATE SET NULLComportamiento similar, pero para la modificación.Si se cambia el ID_Estudiante, el sistema coloca NULL en el campo ID_Estudiante de las matrículas asociadas.

Estas reglas son fundamentales para mantener la coherencia de los datos en toda la base de datos, incluso cuando se realizan operaciones de borrado o modificación.

Integridad de dominio

Cada columna de una tabla almacena un tipo específico de información. La integridad de dominio se encarga de que esos valores sean válidos dentro del conjunto permitido.

Puedes imaginar un dominio como un “espacio seguro” que define qué valores son aceptables. Esta regla asegura cosas como:

  • Que un campo precio no contenga un texto.
  • Que una fecha no tenga un formato incorrecto o imposible.
  • Que un estado definido como activo o inactivo no contenga valores arbitrarios.
  • Que una columna que representa una cantidad no permita números negativos si no tienen sentido.

En SQLite y otros motores esta integridad se consigue mediante tipos de datos, restricciones NOT NULL, reglas UNIQUE, cláusulas CHECK y valores por defecto. No son detalles menores: evitan que tu base de datos acumule errores silenciosos que más tarde derivan en problemas aún mayores.

Integridad lógica o de negocio

Mientras las tres integridades anteriores son reglas estrictamente técnicas, la integridad lógica representa normas derivadas del funcionamiento real del sistema.

Este tipo de integridad asegura que la información tenga sentido dentro del dominio que estás modelando. Algunas reglas típicas ayudan a entenderlo:

  • Una multa no puede marcarse como pagada sin una fecha de pago.
  • Un préstamo no puede cerrarse antes de su fecha de inicio.
  • Un estudiante no puede estar inscrito en un curso inexistente.
  • Un pedido no puede tener un total negativo.

Estas reglas pueden implementarse en la base de datos mediante restricciones o triggers, aunque muchas veces se aplican desde la lógica de la aplicación. Lo importante es que la base de datos no permita estados imposibles o contradictorios.

Ejercicio práctico: Comprobando integridad con Node.js y CSV

Enunciado

Imagina un centro educativo con tres conjuntos de datos:

  • Una lista de alumnos, donde cada alumno debe tener un identificador único.
  • Una lista de cursos, igualmente con identificadores únicos.
  • Una lista de matrículas, que relacionan alumnos con cursos.

El objetivo del ejercicio es detectar automáticamente violaciones de integridad en estos datos.

Vas a detectar los siguientes casos:

  1. Violación de integridad de entidad:

    Alumnos o cursos con claves primarias duplicadas.

  2. Violación de integridad referencial:

    Matrículas que apuntan a alumnos o cursos inexistentes.

  3. Violación de integridad de dominio:

    Valores no válidos, por ejemplo alumnos sin nombre o cursos sin duración.

  4. Violación de integridad lógica:

    Matrículas cuya fecha es anterior al año de inicio del curso.

Será un simulador muy sencillo, pero suficiente para interiorizar los conceptos.

Archivos CSV necesarios

Crea una carpeta llamada datos/ con estos archivos.

alumnos.csv

id_alumno,nombre,email
1,Ana,ana@example.com
2,Luis,luis@example.com
2,Carlos,carlos@example.com
3,,sofia@example.com

Explicación breve:

  • Hay un duplicado de id_alumno (2) → violación de integridad de entidad.
  • El alumno con id 3 no tiene nombre → violación de dominio.

cursos.csv

id_curso,nombre,duracion
10,JavaScript,60
11,Base de Datos,80
12,Python,abc

Explicación:

  • El curso 12 tiene una duración inválida → violación de dominio.

matriculas.csv

id_matricula,id_alumno,id_curso,fecha
100,1,10,2024-01-10
101,3,11,2024-01-12
102,99,10,2024-01-15
103,1,12,2020-01-10
104,2,10,2023-01-01

Explicación:

  • La matrícula 102 apunta al alumno 99, que no existe → violación de integridad referencial.
  • La matrícula 103 apunta al curso 12, que sí existe… pero Python tiene una duración inválida, así que el curso es inválido → también violación de referencia indirecta.
  • Además, la matrícula 103 tiene una fecha 2020-01-10 que no tiene sentido para un curso creado en 2024 → violación de integridad lógica.

Código Node.js para validar todo (ES Modules, sin frameworks)

Crea un archivo validar.js dentro de scripts/.

// validar.js
// Ejercicio de demostración de las reglas de integridad usando Node.js puro.
// Se leerán archivos CSV, se convertirán en objetos y se aplicarán las comprobaciones.
// Todo con comentarios extensos para que puedas entenderlo paso a paso.

import fs from "fs";

// Función genérica para leer un CSV y devolver un array de objetos
function leerCSV(ruta) {
const contenido = fs.readFileSync(ruta, "utf8").trim();
const [cabecera, ...filas] = contenido.split("\n");
const campos = cabecera.split(",");

return filas.map(fila => {
const valores = fila.split(",");
return Object.fromEntries(
valores.map((valor, i) => [campos[i], valor])
);
});
}

// Cargar los datos
const alumnos = leerCSV("./datos/alumnos.csv");
const cursos = leerCSV("./datos/cursos.csv");
const matriculas = leerCSV("./datos/matriculas.csv");

// ---------------------------------------------------------
// 1) INTEGRIDAD DE ENTIDAD: detectar claves duplicadas
// ---------------------------------------------------------

function detectarDuplicados(datos, campoClave, etiqueta) {
const vistos = new Set();
for (const fila of datos) {
const clave = fila[campoClave];
if (vistos.has(clave)) {
console.log(`Violación de integridad de entidad en ${etiqueta}: clave duplicada ${clave}`);
} else {
vistos.add(clave);
}
}
}

detectarDuplicados(alumnos, "id_alumno", "alumnos");
detectarDuplicados(cursos, "id_curso", "cursos");
detectarDuplicados(matriculas, "id_matricula", "matriculas");

// ---------------------------------------------------------
// 2) INTEGRIDAD REFERENCIAL: matriculas que apuntan mal
// ---------------------------------------------------------

const idsAlumnos = new Set(alumnos.map(a => a.id_alumno));
const idsCursos = new Set(cursos.map(c => c.id_curso));

for (const m of matriculas) {
if (!idsAlumnos.has(m.id_alumno)) {
console.log(`Violación de integridad referencial: matrícula ${m.id_matricula} apunta a alumno inexistente ${m.id_alumno}`);
}

if (!idsCursos.has(m.id_curso)) {
console.log(`Violación de integridad referencial: matrícula ${m.id_matricula} apunta a curso inexistente ${m.id_curso}`);
}
}

// ---------------------------------------------------------
// 3) INTEGRIDAD DE DOMINIO: valores inválidos
// ---------------------------------------------------------

for (const a of alumnos) {
if (!a.nombre || a.nombre.trim() === "") {
console.log(`Violación de dominio: alumno ${a.id_alumno} no tiene nombre válido`);
}
}

for (const c of cursos) {
const duracion = parseInt(c.duracion, 10);
if (isNaN(duracion) || duracion <= 0) {
console.log(`Violación de dominio: curso ${c.id_curso} tiene duración inválida (${c.duracion})`);
}
}

// ---------------------------------------------------------
// 4) INTEGRIDAD LÓGICA: fechas incompatibles o absurdas
// ---------------------------------------------------------

// Para simplificar, asumimos que todos los cursos empiezan en 2024
const inicioCursos = {};

// Crear un “catálogo de fechas” de inicio para comprobar lógica
for (const c of cursos) {
inicioCursos[c.id_curso] = "2024-01-01";
}

for (const m of matriculas) {
const fechaMat = new Date(m.fecha);
const inicioCurso = new Date(inicioCursos[m.id_curso]);

if (fechaMat < inicioCurso) {
console.log(`Violación de integridad lógica: matrícula ${m.id_matricula} tiene fecha anterior al inicio del curso ${m.id_curso}`);
}
}

¿Qué obtendrás al ejecutar el script?

En la terminal:

node scripts/validar.js

Verás una salida parecida a:

Violación de integridad de entidad en alumnos: clave duplicada 2
Violación de dominio: alumno 3 no tiene nombre válido
Violación de dominio: curso 12 tiene duración inválida (abc)
Violación de integridad referencial: matrícula 102 apunta a alumno inexistente 99
Violación de integridad lógica: matrícula 103 tiene fecha anterior al inicio del curso 12

Con esto habrás comprobado tú mismo cómo cada regla detecta un tipo de error diferente y por qué son tan importantes.