Domanda

Sto pensando di progettare uno schema di database simile al seguente:

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

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

Person.PrimaryAddressID e Address.PersonID sarebbero chiavi esterne alle tabelle corrispondenti.

L'ovvio problema è che è impossibile inserire qualcosa in entrambe le tabelle. Esiste un modo per progettare uno schema funzionante che imponga a tutte le persone che hanno un indirizzo primario?

È stato utile?

Soluzione

" Credo che sia impossibile. Non è possibile creare un record di indirizzo fino a quando non si conosce l'ID della persona e non è possibile inserire il record di persona fino a quando non si conosce un AddressId per il campo PrimaryAddressId. & Quot;

A prima vista, questa affermazione sembra così allettante. Tuttavia, è piuttosto propostro.

Questo è un tipo molto comune di problema che i fornitori di SQL DBMS hanno già provato ad attaccare per decenni.

La chiave è che tutto il controllo dei vincoli deve essere " differito " fino al completamento di entrambi gli inserti. Ciò può essere ottenuto in diverse forme. Le transazioni del database possono offrire la possibilità di fare qualcosa come & Quot; SET controllo vincolo differito ON & Quot; e il gioco è fatto (se non fosse per il fatto che in questo esempio particolare, probabilmente dovresti pasticciare molto con il tuo design per essere in grado di DEFINIRE i due vincoli FK, perché uno di loro semplicemente NON È un FK "vero" in senso SQL!).

Le soluzioni basate su trigger descritte qui ottengono essenzialmente lo stesso effetto, ma sono esposte a tutti i problemi di manutenzione che esistono con l'integrità applicata dall'applicazione.

Nel loro lavoro, Chris Date & amp; Hugh Darwen descrive qual è la vera soluzione al problema: assegnazione multipla. Questa è, essenzialmente, la possibilità di comporre diverse istruzioni di aggiornamento distinte e di far agire il DBMS su di esso come se fosse un'unica istruzione. Le implementazioni di questo concetto esistono, ma non troverai nessuno che parla di SQL.

Altri suggerimenti

Contrassegniamo l'indirizzo primario nella nostra tabella degli indirizzi e quindi abbiamo trigger che impongono che solo un record per persona possa averlo (ma un record deve averlo). Se si modifica l'indirizzo primario, aggiornerà sia l'indirizzo principale precedente che quello nuovo. Se elimini un indirizzo primario ed esistono altri indirizzi, ne promuoverà uno (basandoti su una serie di regole) all'indirizzo primario. Se l'indirizzo è inserito ed è il primo indirizzo inserito, lo contrassegnerà automaticamente come indirizzo principale.

Questo è un perfetto esempio di relazione molti-a-molti. Per risolvere il problema, è necessario disporre di una tabella PERSON_ADDRESS intermedia. In altre parole;

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)

In questo modo è possibile assegnare più indirizzi a una PERSONA e anche riutilizzare i record di INDIRIZZO in più PERSONA (per membri della famiglia, dipendenti della stessa società, ecc.). Utilizzando il campo is_primary nella tabella PERSON_ADDRESS, puoi identificare se quella combinazione person_addrees è un indirizzo primario per una persona.

Il secondo FK (PersonId dall'indirizzo alla persona) è troppo restrittivo, IMHO. Stai suggerendo che un indirizzo può avere una sola persona?

Dal tuo progetto, sembra che un indirizzo possa essere applicato a una sola persona, quindi usa PersonID come chiave per la tabella degli indirizzi e rilascia il campo Chiave AddressID.

So che probabilmente sarò crocifisso o altro, ma qui va ...

L'ho fatto in questo modo per il mio " quot molto particolare unico e non standard &; bisogno di business (= (Dio sto iniziando a suonare come DDL SQL anche quando parlo).

Ecco un esempio:

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 vita continua ... indipendentemente dal fatto che tu fornisca e indirizzi alla persona e viceversa >

Ho definito una tabella, quindi l'altra tabella che fa riferimento alla prima e quindi ho modificato la prima per riflettere il riferimento alla seconda (che non esisteva al momento della creazione della prima tabella). Non è pensato per un determinato database; se ne ho bisogno, lo provo e se funziona, allora lo uso, altrimenti cerco di evitare di avere quel bisogno nel design (non riesco sempre a controllarlo, a volte il design mi viene consegnato così com'è) . se hai un indirizzo senza persona, allora appartiene al " government " persona. Se hai un & Quot; senzatetto & Quot; quindi ottiene il " casa abbandonata " indirizzo. Eseguo un processo per determinare quali case non hanno utenti

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top