5

Scusate per il titolo, è difficile da spiegare.MySQL: Due relazioni n: 1, ma non entrambe contemporaneamente

Ho bisogno di un modello di dati simile a questo: alt text

Come si può vedere, un set può appartenere sia ad un utente o di una scuola. Il mio problema: dovrebbe essere consentito solo a un utente o una scuola. Ma mai entrambi allo stesso tempo.

Come posso risolvere questo problema?

+0

Che cosa hai usato per creare la grafica? – basszero

+0

@basszero: MySQL Workbench –

+0

Eeeek !!! Ha fatto molta strada dall'ultima volta che l'ho provato! – basszero

risposta

6

Il progetto corrente è denominato archi esclusivi in cui la tabella sets ha due chiavi esterne e ha bisogno esattamente di una di esse per essere non null. Questo è un modo per implementare le associazioni polimorfiche, poiché una determinata chiave esterna può fare riferimento a una sola tabella di destinazione.

Un'altra soluzione è quella di fare una comune "supertable" che entrambi users e schools riferimenti, e quindi utilizzare tale come il genitore di sets.

create table set_owner 

create table users 
    PK is also FK --> set_owner 

create table schools 
    PK is also FK --> set_owner 

create table sets 
    FK --> set_owner 

Si può pensare a questo come analogo a un un'interfaccia in OO modellazione:

interface SetOwner { ... } 

class User implements SetOwner { ... } 

class School implements SetOwner { ... } 

class Set { 
    SetOwner owner; 
} 

Re tuoi commenti:

Quindi la tabella SetOwner contiene sia UserIDs e SchoolIDs, corretto? Ciò significherebbe che non mi sarebbe permesso avere lo stesso ID per un utente e una scuola. Come posso far valere questo?

Lascia che la tabella SetOwners generi valori di identificazione. Devi inserire in SetOwners prima di poterlo inserire in Utenti o Scuole. Quindi inserisci ID in Utenti e scuole non autoincremento; basta usare il valore che è stato generato da SetOwners:

INSERT INTO SetOwners DEFAULT VALUES; -- generates an id 
INSERT INTO Schools (id, name, location) VALUES (LAST_INSERT_ID(), 'name', 'location'); 

questo modo non dato valore id verrà utilizzata sia per una scuola e un utente.

Se mi piacerebbe ottenere il tipo proprietario per un set, ho bisogno di un attributo ownerType in mia tabella SetOwner?

Si può certamente fare questo. Di fatto, potrebbero esserci altre colonne comuni a utenti e scuole e potresti inserire queste colonne in SetOwners superabili. Questo entra nel pattern Class Table Inheritance di Martin Fowler.

E se voglio ottenere il nome della scuola o dell'utente (qualunque sia il tipo), posso farlo con una singola query, o ho bisogno di due query (prima di ottenere il tipo e il secondo per ottenere il nome)?

È necessario effettuare un join. Se stai interrogando da un determinato Set e sai che appartiene a un utente (non a una scuola) puoi saltare a unirsi a SetOwners e unirti direttamente agli Utenti. I join non devono necessariamente passare per le chiavi esterne.

SELECT u.name FROM Sets s JOIN Users u ON s.SetOwner_id = u.id WHERE ... 

Se non si sa se un dato insieme appartiene ad un utente oa una scuola, che avrebbe dovuto fare un outer join a entrambi:

SELECT COALESCE(u.name, sc.name) AS name 
FROM Sets s 
LEFT OUTER JOIN Users u ON s.SetOwner_id = u.id 
LEFT OUTER JOIN Schools sc ON s.SetOwner_id = sc.id 
WHERE ... 

si sa che il SetOwner_id deve abbinare uno o l'altro tavolo, utenti o scuole, ma non entrambi.

+0

Grazie per l'ottima spiegazione. L'approccio con il superabile sembra simile a quello che Geofflane ha proposto nel suo commento. Penso di aver capito il concetto ora. Quindi la tabella 'SetOwner' contiene sia UserIDs che SchoolIDs, corretta? Ciò significherebbe che non mi sarebbe permesso avere lo stesso ID per un utente e una scuola. Come posso far valere questo? –

+0

E un altro aspetto: l'ERD al momento appare come questo http://imgur.com/vD1ln.png.Se desidero ottenere il tipo di proprietario per un set, ho bisogno di un attributo ownerType nella mia tabella SetOwner? O c'è un altro modo per farlo? E se voglio ottenere il nome della scuola o l'utente (qualunque sia il tipo), posso farlo con una singola query, o ho bisogno di due query (prima di ottenere il tipo e il secondo per ottenere il nome)? –

+0

Impressionante modifica, grazie. Ecco perché amo Stackoverflow. C'è solo una domanda rimanente: Posso utilizzare tranquillamente 'LAST_INSERT_ID()' in un ambiente multiutente, oppure le query e gli inserimenti da parte di altri utenti possono interferire con la query corrente, in modo che LAST_INSERT_ID cambi? –

1

Dovrai utilizzare un trigger per applicare questa regola aziendale, perché MySQL ha ma non impone i vincoli CHECK (su qualsiasi motore).

2

Con l'avvertenza: non è del tutto chiaro cosa sia un Set nel modello di dati.

Sto interrogando il tuo modello di dati.

Perché Utente -> Set e Scuola -> Set devono puntare alla stessa tabella. Non è più Normalizzato se si tratta di insiemi di dati realmente indipendenti. Solo perché condividono alcune somiglianze nelle colonne che tracciano non significa che debbano essere memorizzati nella stessa tabella. Sono logicamente la stessa cosa?

È sufficiente creare tabelle separate per School Set e User Set. Ciò renderà anche le query inverse più facili perché non dovrai controllare le relazioni null nella tabella Sets per sapere se si tratta in realtà di un set utente o di un set di scuole.

+0

Grazie per il suggerimento. In realtà i set (molti vocab fanno un set) sono esattamente gli stessi, possono avere solo proprietari diversi, una scuola o un utente. Come un set non verrà mai spostato dall'appartenenza a una scuola all'appartenenza a un utente o viceversa, potrei semplicemente creare due tipi di set e copiare i dati sull'altra tabella quando necessario. Il problema con questo approccio è che allora avrò bisogno di due tipi di vocaboli, e non penso che sarebbe molto sensato. –

+0

Probabilmente potresti inserire una tabella SetOwner tra Utenti/Scuole e Set. Quindi ciascuna delle tabelle che potrebbe 'possedere' un Set avrebbe una colonna FK per un set_owner_id. Anche la tabella Set avrebbe un set_owner_id. Quindi fondamentalmente un utente e una scuola hanno una relazione 1: 1 con SetOwner e SetOwner è 1: m per impostare. Quindi tutto ha integrità referenziale esecutiva. Ciò renderà piuttosto difficile passare da Set a Owner (logico). – geofflane

+0

Intendi così? http://i.imgur.com/dVRs9.png Non capisco davvero come questo risolva il problema. –

Problemi correlati