Duda normalizando una base de datos

¡Hola!

Supongo que alguien que trabaje con bbdd a diario me sabra contestar facil, de hecho no creo que sea una cuestion dificil, pero no se me ocurre como hacerlo. Supongamos que tenemos la Tabla Clientes, uno de cuyos atributos es Telefono, y creamos la Tabla Telefonos. Ya que el atributo telefonos de la tabla clientes es multivalorado, la tabla telefonos tiene dos claves, una foranea al identificador del cliente y otra con el telefono en si mismo (para que un cliente pueda tener varios numeros). Hasta ahi se llegar y es como lo he hecho siempre, pero aqui viene mi duda:

Supongamos que, ademas, tenemos una tercera Tabla Sociedades, uno de cuyos atributos sera Telefonos (tambien multivalorado). ¿Debo crear otra tabla de telefonos que tenga como clave foranea al identificador de la sociedad (ademas de la clave que representa el numero), o hay alguna forma mucho mas sencilla y logica para integrar estos campos dentro de la misma tabla Telefonos y dejarme de lios?

Ya digo que seguro es un problema sencillo para los que trabajeis a diario con bases de datos, pero yo hace mucho que no toco nada de esto y haciendo una pequeña aplicacion en PHP me ha surgido la duda de como diseñar la base de datos para poder implementarla.

¡Mil gracias!
Quien debe tener la clave foránea respecto de la tabla Teléfonos es la tabla Clientes.

Así mismo, la tabla Sociedades debe tener otra clave foránea respecto de la tabla Clientes.

Sería:

Clientes:
Clave principal
Clave foránea -> campo clave principal tabla Teléfonos

Teléfonos:
Clave principal

Sociedades:
Clave principal
Clave foránea -> campo clave principal tabla Teléfonos
Ahhh, mira, eso no se me habia ocurrido a mi nunca y tiene bastante sentido. Es decir, la tabla telefono tiene el autonumerico de rigor y luego el numero en si mismo, y lo que hago es guardar tanto en sociedades como en clientes el id autonumero de la tabla telefono (algo asi como un puntero). Eso es lo que me propones, ¿verdad? Es ciertamente ingenioso....

De todas maneras, si no tuviese la tabla Sociedades, ¿es correcto diseñar la tabla Telefonos como lo hago normalmente (no se si antes me explique correctamente), o es un cutre apaño? Yo juraria que me enseñaron asi, pero vete a saber...
Cancerber escribió:Ahhh, mira, eso no se me habia ocurrido a mi nunca y tiene bastante sentido. Es decir, la tabla telefono tiene el autonumerico de rigor y luego el numero en si mismo, y lo que hago es guardar tanto en sociedades como en clientes el id autonumero de la tabla telefono (algo asi como un puntero). Eso es lo que me propones, ¿verdad? Es ciertamente ingenioso....

De todas maneras, si no tuviese la tabla Sociedades, ¿es correcto diseñar la tabla Telefonos como lo hago normalmente (no se si antes me explique correctamente), o es un cutre apaño? Yo juraria que me enseñaron asi, pero vete a saber...


No es exactamente así. Después de leerte bien me he dado cuenta que no entendí muy bien tu petición.

Sería algo así:

Imagen

Siendo que la tabla teléfonos tendrá 2 foreigns keys, una apuntando a clientes y la otra a sociedades. Siendo evidentemente el valor de ese campo nulo para uno de los dos.

PD: Es correcto diseñar la tabla teléfonos como lo comentas al principio junto con clientes. Es así como hay que hacerlo. ;)
Lo que se suele hacer es (para cada caso individual, por ejemplo clientes) que la tabla teléfonos tenga 2 atributos que forman una PK: el identificador del cliente (y además, es FK) y el teléfono.
Así no podrías utilizar una sola tabla para el caso clientes-sociedades, ya que como todos sabemos un atributo de la PK no puede ser nulo.

Sin embargo, si utilizas códigos autonuméricos en la tabla teléfonos sí que puedes hacerlo, pero pierdes algo de especificación. De hecho, y de forma estricta, los atributos opcionales (como sería el caso, ya que un teléfono es de un cliente O de una sociedad) se "normalizan" en un paso previo a lo que es la normalización en sí. Eso sí, como la tabla teléfono surge después de dicho paso, queda un poco raro.
El problema que tienes es que la tabla que tienes de Teléfonos no es una tabla de maestros (un listado de teléfonos) sino que realmente es una tabla de relación. Te relaciona los clientes con los teléfonos. Esto te sirve si los teléfonos solo "aparecen" con los clientes.
Cuando quieres añadir otra relación de teléfonos con sociedades debes crear otra tabla de relación entre teléfono y sociedad y si quieres (para ser más puritano) y/o te hace falta, crear una tabla de maestro de teléfonos

Yo crearía las tablas:

Clientes: ID_CLIENTE + Otros Datos
Sociedades: ID_SOCIEDAD + Otros Datos
RL_Telefono_Clientes: ID_CLIENTE + ID_TELEFONO
RL_Telefono_Sociedad: ID_SOCIEDAD + ID_TELEFONO

y si quieres un listado de todos los teléfonos

Telefonos: ID_TELEFONO


Espero haberte ayudado


Edit: Me he empezado ha enrollar sin tener en cuenta que seguramente un teléfono solo podrá ser de un cliente o de una sociedad. En ese caso bastaría con la solución que propone siddhartha
Vale, perfecto, ahora si que lo he entendido a la perfeccion. La verdad es que era algo facil, mira que no ocurrirseme...La costumbre de los not null, supongo XD

¡Muchisimas gracias, siddhartha!

EDIT: Acabo de leer a jorcoval y Xtrmdr.

Jorcoval, lo que tu dices (tabla telefonos formada por dos elementos que general la PK) es lo que he venido haciendo siempre. Por eso preguntaba como hacerlo si quiero meter una tercera tabla con Sociedades, si crear la Tabla TelefonoCliente y aparte la TelefonoSociedad o podia hacerlo en una unica tabla con las correspondientes claves foraneas. Lo que no entiendo es lo que comentas en caso de utilizar autonumericos para la Tabla Telefono. ¿Podrias volver a explicarmelo? Gracias.

Xtrmdr, eso que tu propones era una de las opciones que tenia en mente pero no sabia si era la mas correcta, la verdad. Realmente tampoco sera una base de datos muy extensa, y la aplicacion en PHP es para una PYME, dudo que superen nunca los 5000 registros y segun me enseñaron a mi tampoco era demasiado bueno tener tablas "repetidas". En cualquier caso, ya que me surgia la duda a la hora de hacerlo queria saber cual era la forma correcta. Segun veo, hay muchas y todas parecen buenas y ayudan a mantener la integridad referencial.

Gracias a todos, ¡de verdad!
Xtrmdr escribió:
Edit: Me he empezado ha enrollar sin tener en cuenta que seguramente un teléfono solo podrá ser de un cliente o de una sociedad. En ese caso bastaría con la solución que propone siddhartha


Realmente tu solución tampoco me parece incorrecta. Al fin y al cabo la ventaja de tener todo en una tabla es solamente no tenerlo en dos tablas. Desde el punto de vista de normalización no sabría decir que es más correcto. Porque podría darse el caso de que un teléfono pueda ser tanto de un cliente como de una sociedad -que sea necesario dar de alta a un proveedor en las dos tablas-. Con lo cual seguirían valiendo las dos soluciones, a la que he planteado yo habría que añadirle una primary key distinta a la tabla de teléfonos para permitir duplicidades de teléfonos. La tuya sería perfecta.
siddhartha escribió:
Xtrmdr escribió:
Edit: Me he empezado ha enrollar sin tener en cuenta que seguramente un teléfono solo podrá ser de un cliente o de una sociedad. En ese caso bastaría con la solución que propone siddhartha


Realmente tu solución tampoco me parece incorrecta. Al fin y al cabo la ventaja de tener todo en una tabla es solamente no tenerlo en dos tablas. Desde el punto de vista de normalización no sabría decir que es más correcto. Porque podría darse el caso de que un teléfono pueda ser tanto de un cliente como de una sociedad -que sea necesario dar de alta a un proveedor en las dos tablas-. Con lo cual seguirían valiendo las dos soluciones, a la que he planteado yo habría que añadirle una primary key distinta a la tabla de teléfonos para permitir duplicidades de teléfonos. La tuya sería perfecta.



La solución que he propuesto yo solo sería necesaria si alguna de las relaciones (bien con clientes o con sociedades) fuera "muchos a muchos". Es decir si un teléfono pudiera pertenecer a varios clientes a la vez o a varias sociedades a la vez.
Si esto no puede pasar (son relaciones uno a muchos) creo que la solución más correcta sería la que propones tú
Cancerber escribió:Jorcoval, lo que tu dices (tabla telefonos formada por dos elementos que general la PK) es lo que he venido haciendo siempre. Por eso preguntaba como hacerlo si quiero meter una tercera tabla con Sociedades, si crear la Tabla TelefonoCliente y aparte la TelefonoSociedad o podia hacerlo en una unica tabla con las correspondientes claves foraneas. Lo que no entiendo es lo que comentas en caso de utilizar autonumericos para la Tabla Telefono. ¿Podrias volver a explicarmelo? Gracias.

Nada, realmente es una marranada que he visto hacer a menudo para fusionar 2 tablas parecidas: te inventas una clave primaria (generalmente, un autonumérico) y fusionas todos los datos. Por eso decía que pierdes especificación.

Lo mejor es crear 2 tablas como ya han comentado: una con teléfonos de clientes, otra con teléfonos de sociedades.
Sigo con el hilo, aunque ya no es exactamente una duda de normalizacion.

Estoy haciendo ahora la logica de edicion y borrado de los telefonos. Finalmente opte por la solucion de Xtrmdr, con una tabla de maestros y otras para las relaciones (telefono_sociedad, telefono_cliente) etcetera. Ahora mi duda viene sobre cuando debo borrar y editar, y sobre que.

Me explico. Supongamos que tenemos a un cliente Alice y otro Bob. Alice tiene el telefono N1, el N2 y el N4. Bob, el N3 y el N4. Si quiero borrar el telefono de Alice N1, lo mejor seria que borre de la tabla de relacion, y seguiria en la de maestros (serian datos que estan ahi aunque no pertenezca nadie). Luego, supongo que con algo en PL/SQL o un trigger o similiar podria hacer limpieza de los telefonos no metidas en ninguna tabla de relacion y santas pascuas.
Ahora, supongamos que quiero borrar el N4 de Alice. Al borrar de la tabla de relaciones, no se borra de la de maestros y Bob sigue teniendo ese número. Bien.

Y si quiero editar, ¿que debo hacer? Supongamos, de nuevo, Alice con N1, N2 y N4. Bob con N3 y N4. Si desde el formulario de Bob edito N3, debería editarlo sobre la de maestros (y en cascada editaria la tabla de relaciones). Al no tener nadie mas el numero de telefono N3, no pasaría nada. Pero si desde el formulario de Bob edito N4 (tambien sobre maestros), le estaría cambiando el numero a Alice sin querer (por la cascada). ¿Hay que hacer comprobacion de si aparece en mas sitios? ¿Seria un select con group by having count, o algo parecido? ¡La de dudas que saltan cuando haces un proyecto (aunque sea pequeño) que ni te planteas en clase!

No se si voy bien encaminado o no... ¡Gracias!
Hola cancerber.

No se si te he entendido bien, pero...

Si tienes una tabla de relaciones con el cliente y el teléfono tal que así:

ID_CLIENTE ID_TELEF
----------- ---------
1 N1
1 N2
1 N4
2 N3
2 N4

Y tienes una tabla de teléfonos tal que así:

ID_TELEF N_TELEFONO
--------- ------------
N1 245465454
N2 874545410
N3 123456545
N4 554441000

El formulario de Bob (ID_CLIENTE 2), debería aparecer tal que así:

[telefonos de bob]
[123456545] *
[554441000] *

Si modificas el segundo registro [554441000], tal y como está montando todo, debes crear un nuevo registro en la segunda tabla N5, con el nuevo teléfono, y actualizar el campo correspondiente en la primera tabla [2/N4] a [2/N5].

No se qué base de datos es, pero si tienes triggers puedes realizar todas las acciones necesarias.
11 respuestas