2009-03-17 6 views
6

Data una tabella di modelli 'A', che può avere più modelli figlio 'B', di cui 'B' avrà uno o più modelli figlio 'C' .. questo suona semplice, tuttavia ho bisogno di applicarlo per ogni 'A', qualsiasi 'B' deve avere una raccolta univoca di 'C' .. per esempio, C non può essere figlio di due 'B che fanno parte dello stesso genitore' A '.. ma una' C 'può essere figlia di più' B 'dato che ogni' B 'genitore' A 'è distinto ..Miglior design dello schema per la relazione tabella che impone l'integrità

Questo ha senso o dovrei non offuscare il mio scenario? evviva in anticipo!

Nota che so che questa politica verrà applicata nell'applicazione, ma non è possibile che il database non sia in uno stato non valido.

Modifica: ciao a tutti, feedback fantastico quindi, innanzitutto, devo ringraziare tutti voi per aver condiviso le vostre conoscenze con me.

solo per chiarire la situazione, mi spiego lo scenario, ma qui ci sono alcune note:

'A' ha zero o piu 'B', una 'B' è implicitamente associato ad una 'A', e come tale è sempre un figlio di una sola "A". "C" è in qualche modo un'entità radice associata a molti "B" e ad altri elementi nel database.


Heres la vera storia:

Questo è un sito web che contiene molti slip (A), e molti membri (C), una breve può avere molte osservazioni (B), di cui una sottomissione sarà sempre uno o più membri associati. L'idea è che una sottomissione può di fatto essere una collaborazione, e ogni membro non ha più "potere" di qualsiasi altro, ma ci sarà un diverso sistema in atto per convalidare la politica di come i membri lavorano insieme.

Quindi, per breve, un membro può inviare solo una singola submission e una submission può avere molti membri (collaboratori).

Spero che questo aiuti, ma penso che tu mi abbia già dato un sacco di aiuto!

Steve.

+0

+1 per volere che il DBMS imponga il vincolo indipendentemente dall'applicazione o dalle applicazioni. –

+0

Sono con Jonathan su questo, troppe persone considerano solo che l'applicazione faccia questo e abbia dati cattivi come risultato. – HLGEM

+0

Come evidenziato dalla mia risposta, il tuo scenario è abbastanza offuscato e se puoi modificarlo, dovresti. Il numero di sistemi che supportano CREATE ASSERTION è molto limitato. La dichiarazione richiesta per verificare i vincoli è complessa da utilizzare in un trigger. –

risposta

3

Penso che avete bisogno di affermazioni standard SQL, che sono (purtroppo) largamente non implementato dal DBMS attuali.

Tutte le risposte sono d'accordo che ci sono tre tabelle primarie chiamati TableA, TableB e TableC, ognuna contenente la propria colonna ID:

TableA (A_ID PRIMARY KEY, ...) 
TableB (B_ID PRIMARY KEY, ...) 
TableC (C_ID PRIMARY KEY, ...) 

non è chiaro dalla descrizione del problema se un il singolo valore B può avere più voci padre A. È chiaro che una singola C può avere più voci parent B. Se B è legato ad una singola A, la progettazione di TableB può essere modificata per:

TableB (B_ID, ..., A_ID REFERENCES TableA) 

se B può essere associato con diversi A di, allora la connessione è meglio rappresentato da una tabella giunzione:

Inoltre, non è chiaro dalla descrizione se la C associata a una B debba essere uguale per ogni A alla quale la B è associata, o se le diverse A possono fare riferimento allo stesso B e l'insieme di C associate con la B per A1 può essere diverso dal set di C associato alla B per A2. (Naturalmente, se una singola B può essere associata solo ad una A, questo problema è discutibile.)

Ai fini di questa risposta, assumerò che qualsiasi B sia associata a una singola A, quindi la struttura di TableB include A_ID come chiave esterna. Dal momento che un singolo C può essere associato a più B è, la struttura in questione è una nuova tabella che unisce:

B_and_C (B_ID REFERENCES TableB, 
     C_ID REFERENCES TableC, 
     PRIMARY KEY (B_ID, C_ID) 
     ) 

Semplificando (omettendo regole su deferrability e immediatezza) un'asserzione si presenta come:

CREATE ASSERTION assertion_name CHECK (<search_condition>) 

Così , una volta che abbiamo un insieme di decisioni di progettazione, possiamo scrivere un'asserzione per convalidare i dati.tavoli Dato TableA, TableB (con a_id chiave esterna), TableC e B_and_C, il requisito è che il numero di occorrenze di un dato C_ID attraverso una completa A è 1.

CREATE ASSERTION only_one_instance_of_c_per_a CHECK 
(
    NOT EXISTS (
     SELECT A_ID, COUNT(C_ID) 
      FROM TableB JOIN B_and_C USING (C_ID) 
      GROUP BY A_ID 
      HAVING COUNT(C_ID) > 1 
    ) 
) 

[Modificato: Penso che questo è più preciso:

CREATE ASSERTION only_one_instance_of_c_per_a CHECK 
(
    NOT EXISTS (
     SELECT A_ID, C_ID, COUNT(*) 
      FROM TableB JOIN B_and_C USING (C_ID) 
      GROUP BY A_ID, C_ID 
      HAVING COUNT(*) > 1 
    ) 
) 

]

l'insieme delle condizioni di unione varia con altre regole per come sono collegati i tavoli, ma la struttura complessiva vincolo rimane la stessa - non ci deve esistere più di un r eferenza a un dato C_ID per un particolare A_ID.


nei commenti qui sotto, note meandmycode:

ho la sensazione che ci sia un difetto nel mio progetto. La mia logica del mondo reale è che una "B" ha sempre almeno un bambino "C". Questo non ha senso dato che "B" deve esistere prima che il suo bambino possa essere attaccato. Il database consentirebbe al momento una "B" di essere collegata a una "A" senza avere almeno UN "C" .. figlio, sto andando così a revisionare "B" in modo che abbia un campo che si riferisce a il suo figlio primario "C", oltre ad avere una raccolta di ulteriori "C", ma ora ho una collezione che potrebbe includere anche la "C" primaria specificata dalla "B", che sarebbe ... errata.

Esiste uno schema di db che potrebbe inferire una regola "uno o più figli", rispetto a zero o più?

Penso che tu abbia problemi con il tuo modello. È difficile creare una B se esiste già una C che si riferisce alla B appena creata, specialmente se C deve riferirsi solo alle B esistenti. Mi viene in mente la frase "pollo e uova". Quindi, normalmente, permetti a B di avere zero o più C in un contesto come questo.

Non hai ancora stabilito se TableB ha una chiave esterna A_ID o se hai una tabella di collegamento come A_and_B. Se ha una chiave esterna, presumibilmente non puoi creare una B finché non hai creato la A alla quale si riferisce.

Non credo che includere un ID C nella tabella B sia una buona idea, rende l'elaborazione asimmetrica (SQL più difficile). Significa anche che se è necessario eliminare quella C, è necessario aggiornare le cose in modo che uno degli altri riferimenti C venga eliminato dalla tabella in cui si trova attualmente, quindi aggiornare il valore nel record B. È disordinato, per essere educato al riguardo.

Penso che sia necessario modificare la domanda per definire la struttura attuale della tabella che si sta guardando - seguendo le linee mostrate in varie risposte; puoi usare i punti tripli per rappresentare colonne diverse ma irrilevanti. L'asserzione che ho suggerito dovrebbe probabilmente essere implementata come una sorta di innesco - che entra nelle notazioni specifiche del DBMS.


Dalla descrizione modificata di memorie (A), osservazioni (B) e soci (C), è chiaro che una singola presentazione si applica a un solo breve, in modo che gli argomenti possono avere una chiave esterna semplice che identifica il brief per cui è una sottomissione. E un membro può collaborare solo su un invio per un particolare brief. Ci sarà una tabella di 'submission_collaborators' con colonne per identificare l'invio e il membro, la combinazione è la chiave primaria e ogni colonna è una chiave esterna.

Briefs(Brief_ID, ...) 
Submissions(Submission_ID, Brief_ID REFERENCES Briefs, ...) 
Members(Member_ID, ...) 
Submission_Collaborators(Submission_ID REFERENCES Submissions, 
         Member_ID REFERENCES Members, 
         PRIMARY KEY (Submission_ID, Member_ID) 
         ) 

Quindi, il requisito è che la seguente query deve restituire nessuna riga:

SELECT s.brief_id, c.member_id, COUNT(*) 
    FROM submissions AS s JOIN submission_collaborators AS c 
     ON s.submission_id = c.submission_id 
    GROUP BY s.brief_id, c.member_id 
    HAVING COUNT(*) > 1 

Questa è la stessa query che ho inserito nel AFFERMAZIONE CREATE (seconda variante). Puoi estrarre informazioni extra (breve titolo, titolo di presentazione, nome del membro, varie date, ecc.), Ma il nocciolo del problema è che la query mostrata non deve restituire alcun dato.

+0

Jonathan, grazie per la risposta, è molto conciso .. Ho la sensazione che ci sia un difetto nel mio design, la mia logica del mondo reale è che una "B" ha sempre almeno un bambino "C" .. questo non ha senso dato che "B" deve esistere prima che il suo bambino possa essere attaccato. * cont * – meandmycode

+0

Il database consentirebbe al momento una "B" di essere collegata a una "A" senza avere almeno UN "C" .. figlio, io come tale sto andando a rivedere "B" in modo che abbia un campo che si riferisce al suo figlio primario "C", oltre ad avere una collezione figlio di * cont * – meandmycode

+0

"C" aggiuntive, ma ora ho una collezione che potrebbe anche includere la "C" primaria specificata dalla "B", che be .. wrong – meandmycode

0

Aggiungere l'ID di TableA a TableB e aggiungerlo alla chiave primaria e fare la stessa cosa per TableB e TableC.

edit:

credo che la prima parte di questa risposta funzionerà per la A a B vincolo. Tuttavia, avrei quindi messo una tabella di collegamento tra B e C che conteneva anche il PK di A. in questo modo hai un 1: N tra A: B, e i tuoi vincoli vengono quindi applicati.

+0

Questo non funzionerà, perché C può essere un figlio di più B purché non condividano un A. –

+0

@Gamecat - The la ragione che hai dato perché non funziona è esattamente la domanda che stiamo cercando di risolvere, e questa risposta è sbagliata, ma è perché non c'è nulla che impedisca a tutti i Bs di avere C1 come un bambino. – cdeszaq

-1

Non credo che sarete in grado di farlo con semplici vincoli di integrità referenziale dichiarativi. Il modo migliore per applicare la logica potrebbe essere l'uso di trigger per implementare i vincoli di business e il rollback di qualsiasi inserimento o aggiornamento che violi le regole.

+0

Questo può davvero essere fatto. Vedi sotto. I grilletti sono solo fangosi e causano problemi se non sono ben documentati. – cdeszaq

+0

@cdeszaq: quale DBMS lo faresti - e come lo faresti senza un trigger? –

0

Quello che hai è rapporto ternario. Quello che devi fare è avere una tabella che leghi A e B e C insieme nella sua chiave primaria. Poiché le chiavi primarie non possono essere duplicate, ciò imporrà che ci sia solo una C per ogni A, e anche ciascuna B. Questo creerà la collezione unica che stavi cercando.

si ottiene la seguente struttura della tabella:

A's({A_ID}, ...) 
B's({B_ID}, ...) 
C's({C_ID}, ...) 
A_B_C_Relation({[A_ID], [B_ID], [C_ID]}, ...) 

chiavi primarie sono le parentesi graffe, chiavi esterne sono tra parentesi.

Look here per ulteriori informazioni.

+0

In che modo ciò impone "C non può essere figlio di due" B che fanno parte dello stesso genitore "A" "? – Jon

+0

Questo non è abbastanza restrittivo: consente {A = 1, B = 1, C = 1} e {A = 1, B = 2, C = 1} ma le regole dicono che non è permesso. –

3

Penso di aver catturato il tuo modello di relazione qui; Se poi non io voto per unobfuscation:

  • A [{AIUTO}, ...]
  • B [{} BID, AID, ...]
  • C [{CID}, .. .]
  • B_C_Link [{BID, CID}, AID]
    • Ulteriori unico indice (AID, CID)

La notazione utilizza l'indicatore della chiave primaria {}. Così come può avere più Bs (mettendo un AID su B), Bs può avere C (usando una tabella molti-a-molti B_C_Link), e più C non possono appartenere allo stesso A (aggiungendo l'AI ai molti- a-molti tavolo e far rispettare (AID, CID) unicità.

+0

Hai una ridondanza nella tabella B_C_Link. L'AID può essere trovato dal valore di BID in modo che la tabella non sia in BCNF. In effetti, penso che sia solo in 1NF, nemmeno in 2NF dal momento che il BID -> AID è una dipendenza transitiva. Se si accetta quella ridondanza, l'indice extra univoco funziona in modo relativamente pulito. –

+0

Corretto, ciò comporta la denormalizzazione del database. Come si suol dire "Normalizza fino a quando fa male, denormalizza fino a quando non funziona". –

Problemi correlati