Skip to main content

¿Qué es una base de datos relacional?

Este primer módulo no busca que aprendas comandos ni sintaxis. Su objetivo es que pienses como alguien que diseña información con cabeza, no como alguien que amontona datos en cualquier parte.

Vamos a entender por qué existen las bases de datos relacionales, cuándo son útiles y cuáles son sus ideas clave, sin depender de ningún sistema específico.

Concepto base

Una base de datos relacional es un sistema organizado que almacena y gestiona datos estructurados en forma de tablas.

Cada tabla representa un conjunto de entidades del mundo real (personas, productos, reservas, facturas, etc.) y las relaciones entre ellas están definidas de forma explícita.

Esto se basa en un modelo matemático formal: El modelo relacional, introducido por Edgar F. Codd en 1970, que se apoya en teoría de conjuntos y lógica de predicados.

La gran ventaja de esto es que la estructura y las reglas están bien definidas, y no dependen de un programa concreto.

Ejemplo sencillo (mundo real):

PersonaEdadPaís
Ana28España
Luis34México
Sara25Chile

Aquí estamos modelando personas como filas (instancias) y atributos como columnas.

Lo que hace “relacional” a una base de datos

  • Los datos están organizados en tablas (no en documentos, ni en ficheros de texto).
  • Las relaciones entre datos están definidas de forma explícita, no implícita.
  • Cada tabla tiene una clave única para identificar cada fila sin ambigüedades.
  • Las operaciones sobre datos se hacen declarando lo que quieres obtener, no cómo recorrer los datos (esto se llama pensamiento declarativo).
  • La integridad de los datos es responsabilidad del sistema, no de tu aplicación.

Ejemplo de relación:

Clienteid_cliente
Ana1
Luis2
Pedidoid_pedidoid_clientetotal
A0011150.00
A0022235.00

Un pedido está relacionado con un cliente a través de id_cliente.

Ventajas prácticas frente a “archivos sueltos”

Archivos sueltosBase de datos relacional
Duplicación de datosDatos normalizados, sin duplicar
Difícil mantener la coherenciaReglas de integridad definidas
Búsquedas manuales y lentasConsultas rápidas y declarativas
No hay control de acceso estructuradoControl de usuarios y permisos
Crece el caos al aumentar la informaciónEscala con estructuras sólidas

Ejemplo real:

Un negocio que empieza guardando clientes en un Excel… y después no puede:

  • Saber cuántos pedidos hizo cada cliente sin fórmulas complejas.
  • Evitar duplicados (“Luis Gómez” / “Luis G.”).
  • Evitar inconsistencias (“cliente borrado pero pedido activo”).

Una base de datos relacional soluciona esto desde el diseño.

Cuándo usar una base de datos relacional

  • Cuando los datos tienen estructura clara (clientes, facturas, productos, matrículas, etc.).
  • Cuando necesitas consistencia estricta (por ejemplo, en sistemas financieros, administrativos o educativos).
  • Cuando hay relaciones complejas entre entidades.
  • Cuando la información debe ser consultable de múltiples formas.

Cuándo no es la mejor opción

  • Datos extremadamente flexibles, sin esquema fijo (ej. logs, eventos, documentos variables).
  • Grandes volúmenes de datos semi-estructurados sin relaciones complejas.
  • Casos donde la estructura cambia todo el tiempo y no hay integridad estricta requerida.

Esto no significa que no puedas usar una base relacional, pero quizá no sea la más óptima.

Ejercicio práctico guiado (VSCode + archivos planos simulados)

Este ejercicio es conceptual pero lo haremos en tu entorno de trabajo para que entiendas por qué estructurar importa.

IMPORTANTE: Los ejemplos que se visualizan a continuación, usan archivos .csv. Un archivo CSV (Comma-Separated Values) es un archivo de texto plano que guarda datos en forma de tabla.

Cada línea representa un registro y cada columna se separa normalmente por comas, aunque a veces se usan punto y coma o tabuladores según el país o la configuración. Un CSV es simple, universal y compatible con casi todas las herramientas. Se puede abrir con Excel, Google Sheets, LibreOffice, bases de datos, lenguajes de programación, APIs e incluso programas antiguos.

Estructura de carpetas sugerida:

proyecto-relacional/

├── datos/
│ ├── clientes.csv
│ ├── pedidos.csv

├── scripts/
│ └── analisis.js (o .py si prefieres Python)
└── README.md

Contenido inicial de clientes.csv

id_cliente,nombre,correo
1,Ana,ana@example.com
2,Luis,luis@example.com
3,Sara,sara@example.com

Contenido inicial de pedidos.csv

id_pedido,id_cliente,total
A001,1,50.00
A002,2,35.00
A003,1,20.00

Objetivo del ejercicio:

Simular una consulta relacional sin usar un motor.

Por ejemplo:

“Quiero saber cuánto ha gastado cada cliente en total”.

Código ejemplo (JavaScript — analisis.js):

import fs from "fs"; 
// Importamos el módulo nativo "fs" de Node.js.
// Este módulo permite trabajar con el sistema de archivos: leer, escribir,
// eliminar y manipular archivos o directorios. En este caso lo usaremos
// para leer archivos CSV en formato de texto.

function leerCSV(ruta) {
// Esta función recibe una ruta a un archivo CSV y devuelve
// un array de objetos JavaScript construidos a partir del contenido del archivo.

const data = fs.readFileSync(ruta, "utf-8").trim();
// Leemos el archivo de manera síncrona con fs.readFileSync.
// Indicamos "utf-8" para que el contenido se decodifique como texto legible.
// Luego aplicamos ".trim()" para eliminar saltos de línea o espacios
// adicionales al inicio o al final del archivo que podrían causar problemas
// al separar las líneas.

const [cabecera, ...filas] = data.split("\n");
// Dividimos todo el texto en líneas usando .split("\n").
// La primera línea de un CSV suele contener los nombres de las columnas,
// por ejemplo: "id_cliente,nombre,total".
// Mediante destructuración sacamos la cabecera y dejamos el resto de líneas
// en el array "filas".

const campos = cabecera.split(",");
// Convertimos la cabecera en un array de campos.
// Si la cabecera es "id_cliente,nombre,edad", entonces campos será:
// ["id_cliente", "nombre", "edad"].
// Esto nos permitirá generar objetos dinámicamente después.

return filas.map(fila => {
// Recorremos cada fila real de datos del CSV.

const valores = fila.split(",");
// Cada fila la convertimos en un array de valores separándolos por comas.
// Si la fila es "1,Ana,32", se convierte en ["1", "Ana", "32"].

return Object.fromEntries(
valores.map((v, i) => [campos[i], v])
);
// Aquí ocurre la parte más importante.
// Recorremos el array de valores y, para cada valor, buscamos su posición "i".
// Como "campos[i]" contiene el nombre de la columna correspondiente,
// podemos construir pares clave-valor.
//
// Ejemplo:
// campos = ["id_cliente", "nombre", "edad"]
// valores = ["1", "Ana", "32"]
//
// Resultado del map:
// [
// ["id_cliente", "1"],
// ["nombre", "Ana"],
// ["edad", "32"]
// ]
//
// Object.fromEntries convierte todo eso en:
// { id_cliente: "1", nombre: "Ana", edad: "32" }
//
// Así obtenemos objetos lo más parecidos posible a datos estructurados reales.
});
}

const clientes = leerCSV("./datos/clientes.csv");
// Leemos el archivo "clientes.csv" y convertimos su contenido en un array de objetos.
// Cada cliente tendrá propiedades según las columnas del CSV.

const pedidos = leerCSV("./datos/pedidos.csv");
// Hacemos lo mismo para los pedidos. Cada pedido debe tener al menos:
// un id de cliente y un total asociado.

// Creamos un mapa de totales por cliente
const totales = {};
// Este objeto servirá como acumulador.
// La clave será el id del cliente y el valor será la suma total de sus pedidos.

for (const pedido of pedidos) {
// Recorremos todos los pedidos para agregarlos por cliente.

const id = pedido.id_cliente;
// Extraemos el identificador del cliente para saber a quién pertenece el pedido.

const total = parseFloat(pedido.total);
// El total del pedido llega como texto desde el CSV, así que lo convertimos
// a número real usando parseFloat. Si no lo convertimos, las sumas resultarían erróneas
// porque se concatenarían cadenas en vez de sumarse cantidades numéricas.

totales[id] = (totales[id] || 0) + total;
// Acumulamos el total en el objeto "totales".
// Si el cliente aún no tiene un registro previo en "totales", usamos 0 como valor inicial.
// De esta manera no necesitamos inicializar previamente cada cliente.
}

// Mostramos resultados finales
for (const cliente of clientes) {
// Recorremos la lista de clientes y mostramos cuánto ha gastado cada uno.

console.log(
`${cliente.nombre} ha gastado ${totales[cliente.id_cliente] || 0}`
);
// Mostramos el nombre del cliente y el total acumulado.
// Si algún cliente no tiene pedidos en el CSV de pedidos,
// totales[cliente.id_cliente] será undefined, así que usamos 0 por defecto.
}

Salida esperada:

Ana ha gastado 70 €
Luis ha gastado 35 €
Sara ha gastado 0 €

Lo que acabas de hacer manualmente es, en esencia, una operación de JOIN + GROUP BY.

Si tuvieras una base de datos relacional real, esto sería una simple consulta declarativa.

Aquí ves por qué es útil delegar esta lógica al sistema, no hacerla tú cada vez.

Buenas prácticas iniciales

  • Siempre define una clave única para identificar cada fila. No uses nombres, correos ni números aleatorios sin control.
  • Piensa en las relaciones antes de programar.
  • Usa nombres consistentes y significativos.
  • Separa datos de lógica: los datos son de la base, no del código.

Errores comunes de principiantes

  • No definir claves (después no puedes enlazar bien).
  • Duplicar información “por comodidad”.
  • Pensar que “Excel es una base de datos”.
  • Cambiar esquemas sin planificar (rompes relaciones).