Los mismos datos de diferentes entidades en la base de datos – Mejores prácticas – Ejemplo de números de teléfono

Una pregunta bastante simple, si tuviera un sistema que tratara con el personal, los clientes y los proveedores, todos los cuales tenían múltiples números de teléfono posibles, ¿cómo harían para almacenar estos números de una manera agradable y normalizada? He tenido un poco de reflexión y la forma lógica no es saltarme.

    En la mayoría de los casos . . .

    • El “personal” siempre describe a las personas.
    • Algunos clientes son personas.
    • Algunos clientes son empresas (organizaciones).
    • Los “proveedores” generalmente son (¿siempre?) Organizaciones.
    • El personal también puede ser clientes.
    • Los proveedores también pueden ser clientes.

    Existen problemas serios al tener tablas separadas de números de teléfono del personal, números de teléfono del proveedor y números de teléfono del cliente.

    • El personal puede ser clientes. Si cambia el número de teléfono del personal, ¿también se debe actualizar el número de teléfono del cliente? ¿Cómo sabes cuál actualizar?
    • Los proveedores pueden ser clientes. Si cambia el número de teléfono de un proveedor, ¿también es necesario actualizar el número de teléfono del cliente? ¿Cómo sabes cuál actualizar?
    • Debe duplicar y mantener sin error las restricciones para los números de teléfono en cada tabla que almacena números de teléfono.
    • Los mismos problemas surgen cuando cambia el número de teléfono de un cliente. Ahora debe verificar si los números de teléfono del personal y del proveedor también deben actualizarse.
    • Para responder a la pregunta “¿Cuyo número de teléfono es 123-456-7890?”, Debe buscar en “n” tablas diferentes, donde “n” es la cantidad de “clases” diferentes de partes con las que trata. Además del personal, los clientes y los proveedores, piense en “teléfonos del contratista”, “teléfonos del cliente potencial”, etc.

    Necesita implementar un esquema de supertipo / subtipo. (Código PostgreSQL, no rigurosamente probado)

    create table parties ( party_id integer not null unique, party_type char(1) check (party_type in ('I', 'O')), party_name varchar(10) not null unique, primary key (party_id, party_type) ); insert into parties values (1,'I', 'Mike'); insert into parties values (2,'I', 'Sherry'); insert into parties values (3,'O', 'Vandelay'); -- For "persons", a subtype of "parties" create table person_st ( party_id integer not null unique, party_type char(1) not null default 'I' check (party_type = 'I'), height_inches integer not null check (height_inches between 24 and 108), primary key (party_id), foreign key (party_id, party_type) references parties (party_id, party_type) on delete cascade ); insert into person_st values (1, 'I', 72); insert into person_st values (2, 'I', 60); -- For "organizations", a subtype of "parties" create table organization_st ( party_id integer not null unique, party_type CHAR(1) not null default 'O' check (party_type = 'O'), ein CHAR(10), -- In US, federal Employer Identification Number primary key (party_id), foreign key (party_id, party_type) references parties (party_id, party_type) on delete cascade ); insert into organization_st values (3, 'O', '00-0000000'); create table phones ( party_id integer references parties (party_id) on delete cascade, -- Whatever you prefer to distinguish one kind of phone usage from another. -- I'll just use a simple 'phone_type' here, for work, home, emergency, -- business, and mobile. phone_type char(1) not null default 'w' check (phone_type in ('w', 'h', 'e', 'b', 'm')), -- Phone numbers in the USA are 10 chars. YMMV. phone_number char(10) not null check (phone_number ~ '[0-9]{10}'), primary key (party_id, phone_type) ); insert into phones values (1, 'h', '0000000000'); insert into phones values (1, 'm', '0000000001'); insert into phones values (3, 'h', '0000000002'); -- Do what you need to do on your platform--triggers, rules, whatever--to make -- these views updatable. Client code uses the views, not the base tables. -- In current versions of PostgreSQL, I think you'd create some "instead -- of" rules. -- create view people as select t1.party_id, t1.party_name, t2.height_inches from parties t1 inner join person_st t2 on (t1.party_id = t2.party_id); create view organizations as select t1.party_id, t1.party_name, t2.ein from parties t1 inner join organization_st t2 on (t1.party_id = t2.party_id); create view phone_book as select t1.party_id, t1.party_name, t2.phone_type, t2.phone_number from parties t1 inner join phones t2 on (t1.party_id = t2.party_id); 

    Para extender esto un poco más, una tabla para implementar “personal” necesita hacer referencia al subtipo de persona, no al supertipo de parte. Las organizaciones no pueden formar parte del personal.

     create table staff ( party_id integer primary key references person_st (party_id) on delete cascade, employee_number char(10) not null unique, first_hire_date date not null default CURRENT_DATE ); 

    Si los proveedores solo pueden ser organizaciones, no personas, entonces una tabla que implementa proveedores haría referencia al subtipo de organizaciones de manera similar.

    Para la mayoría de las empresas, un cliente puede ser una persona o una organización, por lo que una tabla que implementa clientes debe hacer referencia al supertipo.

     create table customers ( party_id integer primary key references parties (party_id) on delete cascade -- Other attributes of customers ); 

    Creo que la decisión debe basarse en una evaluación práctica de cuán importante es esta información de contacto, con qué frecuencia cambia y cuánta superposición puede haber entre diferentes tipos de personas con números de teléfono.

    Si la información de contacto es volátil y / o realmente central para la aplicación, es probable que una mayor normalización sea mejor. Esto significaría tener una tabla PHONE_NUMBER a la que sus diversas tablas CLIENTE, PROVEEDOR, EMPLEADO (etc.) podrían apuntar, o más probablemente se haga referencia a algún tipo de intersección de tres vías entre tipo de contacto, contacto individual (cliente / proveedor / empleado) y punto de contacto (teléfono). De esta forma, puede hacer que el número de teléfono del hogar de un empleado sea el número de su empresa principal de registros del cliente, y si cambia, se cambia una vez por cada uso de ese punto de contacto.

    Por otro lado, si está almacenando números de teléfono por el simple hecho de que no los usa y probablemente no los mantendrá, entonces pasará mucho tiempo y esfuerzo modelando y construyendo esta sofisticación en su base de datos. Puede valer la pena y puede hacer las buenas y anticuadas columnas Phone1, Phone2, Phone3, … en CUSTOMER, PROVEEDOR, EMPLEADO o lo que sea. Este es un mal diseño de la base de datos pero es una buena práctica de desarrollo del sistema en la medida en que está aplicando la regla 80/20 para identificar las prioridades del proyecto.

    Entonces, para resumir: si los datos importan, hazlo bien, si los datos realmente no importan, solo dales una bofetada, o mejor aún, déjalo fuera del todo.

    La forma más directa es probablemente la mejor. Incluso si un Personal, Cliente o Proveedores tuvieran una ubicación para teléfono, teléfono celular y número de fax, probablemente sea mejor colocar esos campos en cada tabla.

    Pero , cuantos más campos tenga, más debería considerar algún tipo de “herencia” o centralización. Si hay otra información de contacto, así como varios números de teléfono, puede tener estos valores comunes en una tabla centralizada, Contactos . Los campos específicos para ser Cliente, Proveedor, etc. se encontrarían en tablas separadas. La tabla Cliente, por ejemplo, tendría una clave externa ContactID de vuelta a Contactos.