Más ejemplos de tablas que no cumplen con la 1FN

3 comentarios

En el artículo:

https://firebird21.wordpress.com/2013/08/19/diseno-de-bases-de-datos-1fn/

habíamos visto cuales son los requisitos para que una tabla cumpla con la 1FN y también algunos ejemplos de tablas que no las cumplen. En este artículo veremos más ejemplos de tablas donde no se cumple la 1FN.

Ejemplo 1

1FN3

Captura 1. Si haces clic en la imagen la verás más grande

La tabla CLIENTES_TEL que vimos en el artículo anterior cumple con la 1FN … si y solo si los teléfonos son todos locales. Pero si ese no es el caso entonces está mal diseñada, como nos muestra la Captura 2.

1FN8

Captura 2. Si haces clic en la imagen la verás más grande

Como podemos ver en la Captura 2 en la columna CTL_TELEFO no se está cumpliendo con la condición 5 de la 1FN. ¿Por qué no? Porque en ella tenemos tres cosas distintas:

  1. El Código del País (“595” en el ejemplo)
  2. El Código del Area (“21” en el ejemplo)
  3. El Número de Teléfono (“666777” en el ejemplo)

Para que esa tabla cumpla con la condición 5 de la 1FN ésta debería ser su estructura:

1FN9

Captura 3. Si haces clic en la imagen la verás más grande

Como puedes ver se agregó una columna para guardar en ella el Código del País, otra columna para guardar en ella el Código del Área y otra columna para guardar en ella el Número de Teléfono. Ahora sí la tabla CLIENTES_TEL cumple con la 1FN.

Ejemplo 2

1FN10

Captura 4. Si haces clic en la imagen la verás más grande

En este caso es la columna CLI_DIRECC la que no cumple con la condición 5 de la 1FN porque en ella se guardan:

  1. El nombre de la calle (“Avda Colón” en el ejemplo)
  2. El número del edificio (“345” en el ejemplo)
  3. El número del piso (“4º” en el ejemplo)
  4. El nombre del departamento (“C” en el ejemplo)

Por lo que ya viste hasta ahora seguramente sabes que la estructura correcta debería ser:

1FN11

Captura 5. Si haces clic en la imagen la verás más grande

Donde agregamos una columna para la Calle, otra columna para el Número del Edificio, otra columna para el Número del Piso y otra columna para el Nombre del Departamento.

Ahora, la pregunta es: ¿debemos ser tan estrictos en cumplir con la 1FN? ¿es un requisito indispensable que nuestra tabla separe así a las direcciones? Las respuestas están basadas en lo siguiente: si no ordenamos a la tabla CLIENTES por dirección del cliente, ni realizamos búsquedas según la dirección del cliente entonces todo esto es un trabajo innecesario, si nuestra tabla está como muestra la captura 4 entonces ya está bien, no es necesario seguir refinándola. Pero si necesitamos ordenar a la tabla por las direcciones, o si necesitamos saber cuales clientes viven en un Piso 4º entonces sí, es un requisito.

Sería el caso si estamos diseñando una Base de Datos para la Policía o para una empresa de Courier (envío de cartas y encomiendas) porque ellos sí necesitan establecer exactamente la dirección de las personas y de las organizaciones y como llegar hasta ellos lo más rápidamente posible.

Ejemplo 3

1FN12

Captura 6. Si haces clic en la imagen la verás más grande

En este caso nuestra tabla de PRODUCTOS tampoco está en la 1FN. ¿Por qué no? porque en la columna PRD_NOMBRE donde se nombra a los productos se están mezclando 3 cosas:

  1. El tipo de producto (“TELEVISOR” en el ejemplo)
  2. La marca del producto (“PHILIPS”, “TOSHIBA” y “TOKIO” en el ejemplo)
  3. La cantidad de pulgadas del producto (“20”, “29” y “32” en el ejemplo)

Como son tres cosas distintas entonces deberían estar (si seguimos estrictamente la 1FN) en tres columnas distintas. Lo correcto sería tener esos datos de esta manera:

1FN13

Captura 7. Si haces clic en la imagen la verás más grande

1FN14

Captura 8. Si haces clic en la imagen la verás más grande

1FN15

Captura 9. Si haces clic en la imagen la verás más grande

1FN19

Captura 10. Si haces clic en la imagen la verás más grande

1FN16Captura 11. Si haces clic en la imagen la verás más grande

1FN17Captura 12. Si haces clic en la imagen la verás más grande

1FN18

Captura 13. Si haces clic en la imagen la verás más grande

1FN20

Captura 14. Si haces clic en la imagen la verás más grande

Y para obtener los mismos datos que la captura 6 escribiríamos:

SELECT
   P.PRD_IDENTI,
   TRIM(L.LIN_NOMBRE) || ' ' || TRIM(M.MAR_NOMBRE) || ' DE ' || TRIM(E.MED_NOMBRE) AS PRD_NOMBRE
FROM
   PRODUCTOS P
JOIN
   LINEAS L
      ON P.PRD_IDELIN = L.LIN_IDENTI
JOIN
   MARCAS M
      ON P.PRD_IDEMAR = M.MAR_IDENTI
JOIN
   MEDIDAS E
      ON P.PRD_IDEMED = E.MED_IDENTI

1FN21Captura 15. Si haces clic en la imagen la verás más grande

Conclusión:

Que todas tus tablas estén en la 1FN es algo muy bueno pues te asegura que tus aplicaciones sean robustas y confiables pero debes evaluar en cada caso si es conveniente o no ser muy estricto en su implementación. Si tienes dudas entonces … aplica la 1FN porque eso nunca puede ser malo.

Artículos relacionados:

Consideraciones a tener en cuenta al diseñar una Base de Datos

Diseño de bases de datos. 1FN

El índice del blog Firebird21

Diseño de bases de datos. 1FN

7 comentarios

Como hemos visto en este artículo:

https://firebird21.wordpress.com/2013/08/18/consideraciones-a-tener-en-cuenta-al-disenar-una-base-de-datos/

para que una tabla esté bien diseñada debe cumplir con las 6 formas normales. Es un estándar que todos los buenos diseñadores de bases de datos relacionales saben que deben cumplir. Una tabla que no cumpla con las formas normales podría funcionar, de hecho hay millones de tablas en millones de aplicaciones que no las cumplen y están allí usándose diariamente, pero la verdadera eficiencia solamente puede conseguirse si se cumplen las formas normales.

Para escribir menos es usual abreviar los nombres: 1FN significa Primera Forma Normal, 2FN significa Segunda Forma Normal y así sucesivamente.

1FN – Primera Forma Normal

Sirve para asegurar que no hay valores repetidos en la tabla

Una tabla está en 1FN si:

  1. No hay orden de arriba-abajo en las filas
  2. No hay orden izquierda-derecha en las columnas
  3. No hay filas duplicadas
  4. Cada campo tiene un solo valor
  5. Todos los valores de una columna tienen el mismo significado
  6. Los campos que no son clave y que pueden identificarse con una clave, deben identificarse por esa clave

La condición 1 significa que no importa en que orden se guardan las filas. O sea que si escribimos:

INSERT INTO CLIENTES (CLI_IDENTI, CLI_NOMBRE) VALUES (1, ‘Juan Pérez’)

INSERT INTO CLIENTES (CLI_IDENTI, CLI_NOMBRE) VALUES (2, ‘María Benítez’)

o si escribimos:

INSERT INTO CLIENTES (CLI_IDENTI, CLI_NOMBRE) VALUES (2, ‘María Benítez’)

INSERT INTO CLIENTES (CLI_IDENTI, CLI_NOMBRE) VALUES (1, ‘Juan Pérez’)

al consultar los datos debe ser lo mismo. Ninguna de esas formas debe ser mejor que la otra.

La condición 2 significa que no debe importar el orden en el cual se guardan las columnas para poder recuperar correctamente esas columnas. O sea que si escribimos:

INSERT INTO CLIENTES (CLI_IDENTI, CLI_NOMBRE) VALUES (1, ‘Juan Pérez’)

o si escribimos:

INSERT INTO CLIENTES (CLI_NOMBRE, CLI_IDENTI) VALUES (‘Juan Pérez’, 1)

debe ser lo mismo, en ambos casos deberíamos poder consultar correctamente a esa tabla, el orden de grabación de las columnas debe ser intrascendente.

La condición 3 requiere que la tabla tenga una Clave Primaria (Primary Key, en inglés). Si no tuviera una Primary Key entonces podría ocurrir que dos filas fueran idénticas.

Por ejemplo, si en una tabla guardamos el Código del producto vendido, la cantidad vendida y el precio de venta podría ocurrir fácilmente que dos o más veces vendamos el mismo producto, la misma cantidad y por supuesto con el mismo precio. (Varios clientes compraron 1 litro de leche, de la misma marca; por lo tanto coinciden el código, la cantidad, y el precio)

Esta condición también implica que la Primary Key no debe tener valores nulos ya que si pudiera tenerlos entonces podrían existir filas duplicadas.

La condición 4 es la que la mayoría de la gente tiene en mente cuando se habla de la 1FN. Significa que en una columna se deben tener valores atómicos, únicos.

Este es un ejemplo de una fila donde no se cumple con la condición 4 de la 1FN.

1FN1

Captura 1. Si haces clic en la imagen la verás más grande

¿Por qué no se cumple con la condición 4 de la 1FN? Porque en la columna CLI_TELEFO se están guardando dos números telefónicos. Y para que cumpla en una columna se debe guardar un valor único.

Un diseñador inexperto podría pensar en solucionar ese problema de esta manera:

1FN2

Captura 2. Si haces clic en la imagen la verás más grande

O sea agregando una columna para guardar en ella el segundo teléfono. ¿Y si hay clientes que tienen 3, 4, 5, teléfonos? ¿Una columna para cada teléfono? No es lo correcto

La forma correcta de solucionar ese problema es con una tabla adicional (llamada por ejemplo: CLIENTES_TEL) que tenga la siguiente estructura:

1FN3

Captura 3. Si haces clic en la imagen la verás más grande

Y en la cual podríamos agregar todos los números de teléfono que quisiéramos de cada cliente:

1FN4

Captura 4. Si haces clic en la imagen la verás más grande

Entonces cada cliente podría tener 1 teléfono, 2 teléfonos, 14 teléfonos, lo que sea, nunca tendríamos problema porque todos esos teléfonos se guardan en nuestra tabla CLIENTES_TEL.

La condición 5 implica que cuando miras a una columna siempre debes saber el significado de lo que estás mirando. No puedes guardar en la misma columna a veces un e-mail, a veces un teléfono, a veces una fecha, etc.

En la tabla de CLIENTES que usamos para estos ejemplos la columna CLI_NOMBRE no cumple con la condición 5 de la 1FN:

¿Por qué la columna CLI_NOMBRE no cumple con la condición 5 de la 1FN?

Porque en ella estamos guardando dos cosas distintas: el nombre del cliente y el apellido del cliente.

Una estructura mejor sería la siguiente:

1FN5

Captura 5. Si haces clic en la imagen la verás más grande

Como puedes ver ahora tenemos una columna para los Nombres y otra columna para los Apellidos. Sin duda que hemos mejorado pero … este cliente tiene 2 apellidos y hay muchos clientes que tienen 2 nombres e inclusive 3 nombres. ¿Debemos seguir refinando nuestra estructura para tener una columna para cada nombre y una columna para cada apellido? Si vas a seguir al pie de la letra la condición 5 de la 1FN entonces sí, pero para estos casos es perfectamente admisible terminar aquí, salvo que por algún motivo necesites consultar por el segundo nombre o por el segundo apellido, entonces sí que deberías tener columnas adicionales para ellos.

La condición 6 establece que siempre que una fila tenga un identificador debe usarse ese identificador y no otras columnas de esa fila.

Por ejemplo, si nuestra tabla CLIENTES_TEL tuviera esta estructura, estaría muy mal:

1FN7

Captura 6. Si haces clic en la imagen la verás más grande

En la Captura 6 vemos un ejemplo de lo que no debe hacerse, eso está terriblemente mal. ¿Por qué? porque cada cliente puede ser identificado mediante una columna llamada CLI_IDENTI que es su Primary Key, y siempre debemos usar esa Primary Key para identificarlo, no su nombre, ni su apellido, ni su e-mail, ni su número de documento de identidad ni alguna otra columna. Debemos usar la Primary Key y nada más que la Primary Key, siempre, en el 100% de los casos.

Cumpliendo con la 1FN

No debes preocuparte por la condición 1 ni por la condición 2 de la 1FN ya que el propio Firebird se encarga de eso, nunca tendrás problemas allí. La condición 3 sí ya te compete, todas tus tablas deben tener una Primary Key y lo mejor es usar para ellas una columna autoincremental, de esa manera te olvidas de asignarle valores a esa columna ya que nuevamente será el Firebird quien se encargará de esa tarea.

1FN6

Captura 7. Si haces clic en la imagen la verás más grande

La condición 4 es de tu absoluta incumbencia, el Firebird no puede ayudarte con eso, él no puede saber si en una columna se está introduciendo un solo valor o varios valores (un solo teléfono o varios teléfonos, según nuestro ejemplo). Por lo tanto es tu responsabilidad como diseñador impedir que tal cosa ocurra.

La condición 5 también es total responsabilidad tuya. Tampoco el Firebird puede saber si todos los valores dentro de una columna tienen el mismo significado o si tienen significados diferentes (nombres y apellidos en una sola columna, como en nuestro ejemplo).

La condición 6 es también responsabilidad toda tuya, el Firebird no puede saber si siempre usas la Primary Key para identificar a las filas de una tabla (que es lo que debes hacer el 100% de las veces) o si a veces utilizas otras columnas (algo que nunca deberías hacer).

NOTA:

Como la 1FN fue inicialmente introducida para restringir relaciones y no tablas entonces hay discrepancias entre los autores sobre cuales condiciones debe reunir una tabla para ser considerada que cumple con la 1FN. En lo que todos están de acuerdo es que debe cumplir con la condición 4.

Conclusión:

Si todas tus tablas cumplen con la 1FN entonces no tendrás valores repetidos, ni columnas con varios significados y estarás seguro de poder identificar a cada fila de cada tabla sin equivocación posible.

Artículos relacionados:

Consideraciones a tener en cuenta al diseñar una Base de Datos

Entendiendo a las Primary Keys

El índice del blog Firebird21