Pregunta

Estoy pensando en diseñar un esquema de base de datos similar al siguiente:

Person (
  PersonID int primary key,
  PrimaryAddressID int not null,
  ...
)

Address (
  AddressID int primary key,
  PersonID int not null,
  ...
)

Person.PrimaryAddressID y Address.PersonID serían claves foráneas para las tablas correspondientes.

El problema obvio es que es imposible insertar algo en cualquiera de las tablas. ¿Hay alguna forma de diseñar un esquema de trabajo que haga cumplir a cada persona que tenga una dirección principal?

¿Fue útil?

Solución

" Creo que esto es imposible. No puede crear un registro de dirección hasta que conozca el ID de la persona y no puede insertar el registro de persona hasta que conozca un AddressId para el campo PrimaryAddressId. & Quot;

A primera vista, esa afirmación parece TAN atractiva. Sin embargo, es bastante propicio.

Este es un tipo de problema muy común que los proveedores de SQL DBMS han estado tratando de atacar desde hace quizás décadas.

La clave es que todas las comprobaciones de restricciones deben ser " diferido " hasta que ambos insertos estén listos. Eso se puede lograr bajo diferentes formas. Las transacciones de la base de datos pueden ofrecer la posibilidad de hacer algo como & Quot; SET verificando la restricción diferida en & Quot ;, y ya está (si no fuera por el hecho de que en este ejemplo en particular, probablemente tendría que jugar muy duro con su diseño para poder DEFINIR las dos restricciones FK, ¡porque una de ellas simplemente NO ES una FK 'verdadera' en el sentido SQL!).

Las soluciones basadas en disparadores como se describen aquí logran esencialmente el mismo efecto, pero están expuestas a todos los problemas de mantenimiento que existen con la integridad aplicada por la aplicación.

En su trabajo, Chris Date & amp; Hugh Darwen describe cuál es la verdadera solución al problema: la asignación múltiple. Esa es, esencialmente, la posibilidad de componer varias declaraciones de actualización distintas y hacer que el DBMS actúe sobre ella como si fuera una sola declaración. Existen implementaciones de ese concepto, pero no encontrará ninguna que hable SQL.

Otros consejos

Marcamos la dirección principal en nuestra tabla de direcciones y luego tenemos activadores que hacen cumplir que solo un registro por persona puede tenerla (pero un registro debe tenerla). Si cambia la dirección principal, actualizará la dirección principal anterior y la nueva. Si elimina una dirección principal y existen otras direcciones, promoverá una de ellas (basada en una serie de reglas) a la dirección principal. Si se inserta la dirección y se inserta la primera dirección, la marcará automáticamente como la dirección principal.

Este es un ejemplo perfecto de relación de muchos a muchos. Para resolver eso, debe tener una tabla PERSON_ADDRESS intermedia. En otras palabras;

PERSON table
person_id (PK)

ADDRESS table
address_id (PK)

PERSON_ADDRESS
person_id (FK) <= PERSON
address_id (FK) <= ADDRESS
is_primary (BOOLEAN - Y/N)

De esta forma, puede asignar múltiples direcciones a una PERSONA y también reutilizar registros de DIRECCIÓN en varias PERSONAS (para miembros de la familia, empleados de la misma compañía, etc.). Usando el campo is_primary en la tabla PERSON_ADDRESS, puede identificar si esa combinación person_addrees es una dirección principal para una persona.

El segundo FK (PersonId de dirección a persona) es demasiado restrictivo, en mi humilde opinión. ¿Estás sugiriendo que una dirección solo puede tener una sola persona?

Según su diseño, parece que una dirección se puede aplicar a una sola persona, por lo tanto, use el PersonID como la clave de la tabla de direcciones y suelte el campo de la clave AddressID.

Sé que probablemente seré crucificado o lo que sea, pero aquí va ...

Lo he hecho así para mi " particular muy propio, único y no estándar " necesidad comercial (= (Dios, estoy empezando a sonar como SQL DDL incluso cuando hablo).

Aquí hay un ejemplo:

CREATE TABLE IF NOT EXISTS PERSON(
    ID INT, 
    CONSTRAINT PRIMARY KEY (ID), 
    ADDRESS_ID INT NOT NULL DEFAULT 1, 
    DESCRIPTION VARCHAR(255), 
    CONSTRAINT PERSON_UQ UNIQUE KEY (ADDRESS_ID, ...));

INSERT INTO PERSON(ID, DESCRIPTION) 
    VALUES (1, 'GOVERNMENT');

CREATE TABLE IF NOT EXISTS ADDRESS(
    ID INT, 
    CONSTRAINT PRIMARY KEY (ID), 
    PERSON_ID INT NOT NULL DEFAULT 1, 
    DESCRIPTION VARCHAR(255), 
    CONSTRAINT ADDRESS_UQ UNIQUE KEY (PERSON_ID, ...), 
    CONSTRAINT ADDRESS_PERSON_FK FOREIGN KEY (PERSON_ID) REFERENCES PERSON(ID));

INSERT INTO ADDRESS(ID, DESCRIPTION) 
    VALUES (1, 'ABANDONED HOUSE AT THIS ADDRESS');

ALTER TABLE PERSON ADD CONSTRAINT PERSON_ADDRESS_FK FOREIGN KEY (ADDRESS_ID) REFERENCES ADDRESS(ID);

< ... la vida continúa ... ya sea que proporcione y dirija o no a la persona y viceversa >

Definí una tabla, luego la otra tabla haciendo referencia a la primera y luego modifiqué la primera para reflejar la referencia a la segunda (que no existía en el momento de la creación de la primera tabla). No es para una base de datos en particular; si lo necesito solo lo pruebo y si funciona, lo uso, si no, trato de evitar tener esa necesidad en el diseño (no siempre puedo controlar eso, a veces el diseño me lo entrega como está) . si tiene una dirección sin una persona, entonces pertenece al " gobierno " persona. Si tiene una & Quot; persona sin hogar & Quot; entonces obtiene el " casa abandonada " dirección. Ejecuto un proceso para determinar qué casas no tienen usuarios

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top