Normalización y Control de Redundancia en Bases de Datos

Capítulo 5

Tiempo estimado de lectura: 10 minutos

+ Ejercicio

Qué almacenar vs qué derivar (criterio para evitar redundancia)

Normalizar no significa “partir tablas porque sí”, sino decidir con criterio qué datos deben persistirse y cuáles pueden calcularse de forma confiable. La redundancia (guardar el mismo hecho en varios lugares) suele introducir anomalías: actualizaciones incompletas, inserciones imposibles o borrados que eliminan información valiosa.

Regla práctica: guarda hechos atómicos, deriva resultados

  • Guarda hechos que provienen del mundo real y que no se pueden reconstruir sin pérdida: fecha de una compra, precio unitario acordado, cantidad, estado de un pedido, dirección declarada por el cliente en ese momento (si es “snapshot”).
  • Deriva valores que son función determinística de otros datos y que no aportan un hecho nuevo: total = cantidad × precio_unitario, edad = hoy − fecha_nacimiento, stock_actual = entradas − salidas.

Checklist para decidir

  • ¿Se puede recalcular siempre? Si sí, preferir derivar.
  • ¿El cálculo depende del tiempo o de reglas cambiantes? Si cambia con el tiempo (impuestos, descuentos, tipo de cambio), quizá convenga guardar el resultado aplicado (por auditoría) y también los insumos.
  • ¿Hay riesgo de divergencia? Si el mismo dato se guardará en dos sitios, necesitas una estrategia explícita de consistencia (triggers, procesos, restricciones, jobs).
  • ¿Es un “snapshot” histórico? Si necesitas conservar el valor tal como era (p. ej., dirección de envío usada en una orden), guarda una copia controlada en el contexto transaccional.

Dependencias funcionales (DF): la herramienta para evaluar calidad

Una dependencia funcional se escribe como X → Y y significa: “si conozco X, entonces Y queda determinado”. En términos prácticos, X identifica unívocamente a Y dentro de una tabla.

Ejemplo simple

Supón una tabla Empleado con columnas: (id_empleado, dni, nombre, fecha_nacimiento, id_departamento, nombre_departamento).

  • id_empleado → dni, nombre, fecha_nacimiento, id_departamento
  • id_departamento → nombre_departamento

Si en la misma fila guardas nombre_departamento, estás repitiendo un dato que depende de id_departamento, no del empleado. Eso es una señal de redundancia y potencial inconsistencia (un departamento renombrado obliga a actualizar muchas filas).

Cómo usar DF para detectar problemas

  • Busca atributos que “pertenecen” a otra cosa: si un atributo depende de una parte del identificador o de otro atributo no clave, probablemente está mal ubicado.
  • Identifica determinantes: columnas que “mandan” sobre otras (códigos de catálogo, estados, ids).
  • Verifica si hay múltiples fuentes del mismo hecho: por ejemplo, guardar estado_nombre además de estado_id.

Anomalías típicas por redundancia

TipoSíntomaEjemplo
ActualizaciónCambiar un dato exige modificar muchas filasRenombrar un estado/provincia en miles de registros
InserciónNo puedes registrar algo sin inventar otros datosNo puedes crear un departamento si no existe aún un empleado
BorradoEliminar una fila borra información que no debería perderseEliminar el último empleado de un departamento elimina el nombre del departamento

Primera Forma Normal (1FN): valores atómicos y sin grupos repetidos

1FN exige que cada columna contenga valores atómicos (no listas) y que no existan grupos repetidos del mismo tipo de dato en columnas separadas.

Continúa en nuestra aplicación.
  • Escuche el audio con la pantalla apagada.
  • Obtenga un certificado al finalizar.
  • ¡Más de 5000 cursos para que explores!
O continúa leyendo más abajo...
Download App

Descargar la aplicación

Síntomas comunes de no cumplir 1FN

  • Columnas como telefonos con “555-111, 555-222”.
  • Columnas repetidas: telefono1, telefono2, telefono3.
  • Campos compuestos difíciles de consultar: direccion en un solo texto cuando necesitas filtrar por ciudad o código postal (ojo: a veces un texto libre es válido si no se consulta por partes).

Corrección paso a paso (caso teléfonos)

Tabla original:

Cliente(id_cliente, nombre, telefonos)

Problema: un cliente puede tener N teléfonos; el atributo no es atómico.

Solución:

Cliente(id_cliente, nombre)  TelefonoCliente(id_cliente, telefono, tipo)
  • TelefonoCliente permite múltiples filas por cliente.
  • Opcional: una clave sustituta id_telefono si necesitas editar/identificar teléfonos individualmente.

Verificación posterior

  • Cada celda contiene un solo valor.
  • Agregar un teléfono no requiere alterar el esquema ni columnas nuevas.

Segunda Forma Normal (2FN): sin dependencias parciales (cuando hay clave compuesta)

2FN aplica cuando una tabla tiene clave primaria compuesta. Exige que todo atributo no clave dependa de la clave completa, no de una parte.

Síntomas comunes de no cumplir 2FN

  • Tablas de “detalle” que repiten datos del “encabezado” o de catálogos.
  • Atributos que dependen solo de id_producto dentro de una tabla cuyo PK es (id_orden, id_producto).

Corrección paso a paso (caso orden y producto)

Tabla problemática:

DetalleOrden(id_orden, id_producto, nombre_producto, precio_lista, cantidad)

Clave: (id_orden, id_producto).

Dependencias:

  • id_producto → nombre_producto, precio_lista (dependen solo de parte de la clave)
  • (id_orden, id_producto) → cantidad

Solución:

Producto(id_producto, nombre_producto, precio_lista)  DetalleOrden(id_orden, id_producto, cantidad, precio_unitario_aplicado)
  • precio_unitario_aplicado puede ser distinto de precio_lista por descuentos o por historial; es un hecho de la transacción, no del catálogo.

Verificación posterior

  • En DetalleOrden no quedan atributos que dependan solo de id_producto o solo de id_orden.
  • Los datos de catálogo se actualizan en un solo lugar (Producto).

Tercera Forma Normal (3FN): sin dependencias transitivas (atributos no clave no determinan otros no clave)

3FN exige que los atributos no clave dependan solo de la clave, y no de otros atributos no clave. El patrón típico es: PK → A y A → B, entonces B está “colgado” de A y debería ir a otra tabla.

Síntomas comunes de no cumplir 3FN

  • Guardar nombre_estado junto con estado_id.
  • Guardar ciudad y provincia cuando codigo_postal ya determina esos datos (según el país y el sistema postal).
  • Guardar datos de “catálogo” embebidos en tablas transaccionales.

Corrección paso a paso (caso estados/provincias)

Tabla problemática:

Cliente(id_cliente, nombre, estado_id, estado_nombre)

Dependencias:

  • id_cliente → estado_id
  • estado_id → estado_nombre (transitiva: id_cliente → estado_nombre vía estado_id)

Solución:

Estado(estado_id, estado_nombre)  Cliente(id_cliente, nombre, estado_id)

Verificación posterior:

  • El nombre del estado existe una sola vez.
  • Renombrar un estado no requiere tocar filas de Cliente.

Casos reales típicos y decisiones correctas

Direcciones: “normalizar” sin perder flexibilidad

Las direcciones son un caso donde conviven necesidades de consulta, variabilidad por país y requisitos de auditoría.

  • Si necesitas buscar/filtrar por ciudad, código postal o región: separa componentes (calle, numero, ciudad, codigo_postal, region, pais).
  • Si necesitas preservar exactamente lo que el usuario escribió (envíos, facturación legal): guarda también un texto_formateado o “líneas” tal cual, además de componentes estructurados.
  • Si una orden debe conservar la dirección histórica: no dependas solo de la dirección “actual” del cliente; crea una entidad de dirección para la orden (snapshot) o referencia a una versión.

Ejemplo aplicado:

Cliente(id_cliente, ...)  Direccion(id_direccion, id_cliente, tipo, calle, numero, ciudad, region, codigo_postal, pais, texto_formateado, vigente_desde, vigente_hasta)  Orden(id_orden, id_cliente, id_direccion_envio_snapshot, ...)

Catálogos: códigos y descripciones

Catálogos (estados, tipos, categorías) suelen causar redundancia cuando se copian descripciones en tablas operativas.

  • Guarda en la tabla operativa solo el identificador (estado_id).
  • La descripción vive en el catálogo (Estado).
  • Si necesitas “congelar” la descripción por auditoría (p. ej., nombre del plan en el momento de la compra), guarda un snapshot controlado en la transacción.

Estados y flujos: evita duplicar “estado actual” y “historial” sin reglas

Un error común es guardar estado_actual en la tabla principal y también un historial de estados, sin garantizar consistencia.

  • Opción A: solo historial y el estado actual se deriva como el último evento (con índice adecuado).
  • Opción B: estado actual persistido + historial, pero con reglas: cada inserción en historial actualiza el estado actual en una transacción atómica.

Ejemplo:

Pedido(id_pedido, estado_id_actual, ...)  PedidoEstado(id_pedido, estado_id, fecha, usuario_id)

Totales calculados: cuándo derivar y cuándo persistir

Derivar el total suele ser lo correcto:

TotalPedido = SUM(Detalle.cantidad * Detalle.precio_unitario_aplicado)

Persistir puede ser aceptable si:

  • El total se consulta masivamente y el cálculo es costoso.
  • Necesitas auditoría del total cobrado (incluyendo impuestos, redondeos, promociones) tal como se aplicó.
  • Hay un proceso claro de recalcular/validar cuando cambian líneas.

Si persistes, define reglas de consistencia:

  • Restricción: no permitir modificar detalles si el pedido está “cerrado”.
  • Trigger o lógica de aplicación: al insertar/actualizar/borrar un detalle, recalcular total.
  • Job de verificación: proceso periódico que compara total persistido vs total derivado y reporta diferencias.

Guía práctica: normalizar una tabla paso a paso (1FN → 3FN)

Paso 1: identifica la clave y el “hecho” que representa la fila

  • Define qué evento/registro representa una fila (p. ej., “una línea de orden”).
  • Determina la clave candidata (simple o compuesta).

Paso 2: lista dependencias funcionales observables

  • Para cada atributo, pregúntate: “¿de qué depende realmente?”
  • Escribe DF del tipo determinante → dependientes.

Paso 3: corrige 1FN

  • Elimina listas y grupos repetidos creando tablas hijas.
  • Separa atributos multivaluados (teléfonos, emails, etiquetas).

Paso 4: corrige 2FN (si hay clave compuesta)

  • Si un atributo depende solo de parte de la clave, muévelo a una tabla donde esa parte sea clave.
  • Deja en la tabla original solo lo que depende de la clave completa.

Paso 5: corrige 3FN

  • Si un atributo no clave determina a otro (dependencia transitiva), crea una tabla para ese determinante.
  • Reemplaza descripciones repetidas por claves foráneas a catálogos.

Paso 6: verificación posterior (pruebas de anomalías)

  • Actualización: ¿puedo cambiar un nombre de catálogo en un solo lugar?
  • Inserción: ¿puedo crear un catálogo sin depender de transacciones?
  • Borrado: ¿borrar una transacción no elimina definiciones maestras?
  • Consistencia: ¿hay un único “origen de verdad” para cada hecho?

Desnormalización controlada: cuándo es aceptable y cómo mantener consistencia

La desnormalización es introducir redundancia de forma deliberada para mejorar rendimiento o simplificar lecturas, asumiendo el costo de mantener consistencia. Es aceptable cuando el beneficio es medible y existe un mecanismo robusto para evitar divergencias.

Escenarios típicos aceptables

  • Campos derivados para lectura rápida: total_pedido, saldo_cliente, stock_disponible.
  • Datos de referencia “congelados” en transacciones: nombre del producto en la factura, tasa de impuesto aplicada, dirección de envío usada.
  • Tablas de resumen (materialized views o tablas agregadas) para reportes.

Reglas y procesos para que no se rompa

  • Actualización atómica: cambios en detalle y en el agregado deben ocurrir en la misma transacción.
  • Fuente de verdad definida: especifica qué columna es derivada y cuál es la base.
  • Restricciones de edición: estados del flujo que bloquean modificaciones (p. ej., factura emitida).
  • Reconciliación: proceso periódico que recalcula desde la fuente y corrige/reporta diferencias.
  • Auditoría: registrar quién y cuándo cambió valores críticos.

Mini-ejemplo: total persistido con verificación

Pedido(id_pedido, total_persistido, moneda, estado_id, ...)  DetallePedido(id_pedido, linea, cantidad, precio_unitario_aplicado, impuesto_aplicado)
  • Regla: total_persistido se recalcula al cambiar DetallePedido mientras estado_id sea “abierto”.
  • Verificación: consulta de control compara total_persistido vs suma derivada y marca discrepancias.

Ahora responde el ejercicio sobre el contenido:

En una tabla DetalleOrden con clave primaria compuesta (id_orden, id_producto), ¿qué cambio ayuda a cumplir 2FN y reducir redundancia?

¡Tienes razón! Felicitaciones, ahora pasa a la página siguiente.

¡Tú error! Inténtalo de nuevo.

2FN exige que los atributos no clave dependan de la clave completa. En una PK compuesta, datos que dependen solo de id_producto (como nombre o precio de lista) deben ir a Producto, dejando en el detalle solo atributos propios de la línea.

Siguiente capítulo

Modelado Lógico Relacional: Tablas, Claves y Tipos de Datos

Arrow Right Icon
Portada de libro electrónico gratuitaModelado de Datos desde Cero: Fundamentos para Bases de Datos Profesionales
56%

Modelado de Datos desde Cero: Fundamentos para Bases de Datos Profesionales

Nuevo curso

9 páginas

Descarga la aplicación para obtener una certificación gratuita y escuchar cursos en segundo plano, incluso con la pantalla apagada.