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_departamentoid_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_nombreademás deestado_id.
Anomalías típicas por redundancia
| Tipo | Síntoma | Ejemplo |
|---|---|---|
| Actualización | Cambiar un dato exige modificar muchas filas | Renombrar un estado/provincia en miles de registros |
| Inserción | No puedes registrar algo sin inventar otros datos | No puedes crear un departamento si no existe aún un empleado |
| Borrado | Eliminar una fila borra información que no debería perderse | Eliminar 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.
- Escuche el audio con la pantalla apagada.
- Obtenga un certificado al finalizar.
- ¡Más de 5000 cursos para que explores!
Descargar la aplicación
Síntomas comunes de no cumplir 1FN
- Columnas como
telefonoscon “555-111, 555-222”. - Columnas repetidas:
telefono1,telefono2,telefono3. - Campos compuestos difíciles de consultar:
direccionen 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)TelefonoClientepermite múltiples filas por cliente.- Opcional: una clave sustituta
id_telefonosi 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_productodentro 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_aplicadopuede ser distinto deprecio_listapor descuentos o por historial; es un hecho de la transacción, no del catálogo.
Verificación posterior
- En
DetalleOrdenno quedan atributos que dependan solo deid_productoo solo deid_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_estadojunto conestado_id. - Guardar
ciudadyprovinciacuandocodigo_postalya 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_idestado_id → estado_nombre(transitiva:id_cliente → estado_nombrevíaestado_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_formateadoo “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_persistidose recalcula al cambiarDetallePedidomientrasestado_idsea “abierto”. - Verificación: consulta de control compara
total_persistidovs suma derivada y marca discrepancias.