8

Sto solo cercando di capire il modo migliore per progettare il mio tavolo per il seguente scenario:tabella del database generico disegno

ho diverse aree nel mio sistema (documenti, progetti, gruppi e clienti) e ciascuno di questi possono avere commenti registrati contro di loro.

La mia domanda è dovrei avere una tabella come questa:

CommentID 
DocumentID 
ProjectID 
GroupID 
ClientID 
etc 

Dove solo uno degli ID avranno dei dati e il resto sarà NULL o devo avere una tabella CommentType separato e avere il mio tavolo commenti così:

CommentID 
CommentTypeID 
ResourceID (this being the id of the project/doc/client) 
etc 

I miei pensieri sono che l'opzione 2 sarebbe più efficiente dal punto di vista dell'indicizzazione. È corretto?

+0

È possibile registrare un singolo commento su più di un elemento? Ad esempio un documento e un progetto o due documenti? –

+0

Nessun commento può essere solo contro un elemento – Gazeth

risposta

0

Tra le opzioni si danno, vorrei andare per il numero 2.

3

Dal punto di vista chiave esterna, il primo esempio è meglio perché si può avere più vincoli di chiave esterna su una colonna, ma il dato deve esistere in tutti quei riferimenti È anche più flessibile se cambiano le regole aziendali.

+0

+1 ... Buon punto sul problema delle chiavi esterne. –

+1

Non è solo un problema imporre le chiavi esterne ma per recuperare tutti i dati, è necessario unirsi alla stessa tabella quattro volte. Può anche avere quattro tabelle di commenti separati che sono specializzate come uso, perché almeno è possibile utilizzare i vincoli FK quindi. Le tabelle EAV in generale non si adattano bene. – HLGEM

5

Leggi sulla normalizzazione del database.

Nulls nel modo in cui descrivi sarebbe una grande indicazione che il database non è progettato correttamente.

Devi dividere tutti i tuoi tavoli in modo che i dati contenuti in essi siano completamente normalizzati, questo ti farà risparmiare un sacco di tempo oltre la linea garantita, ed è molto più pratico abituarsi.

+4

Abbastanza sicuro di aver eseguito correttamente la "normalizzazione", sono nel Regno Unito. –

+1

Scusate :) Stavo correggendo un paio di errori di battitura e il mio correttore ortografico li ha evidenziati. Ho pensato che potesse essere il caso, ma ho pensato che forse ti avrei aiutato i britannici a vedere l'errore dei tuoi modi;) –

3

Per continuare da @OMG Ponies' answer, ciò che si descrive nel secondo esempio viene chiamato un polimorfico Associazione, in cui la chiave esterna ResourceID possono fare riferimento le righe in più di una tabella. Tuttavia, nei database SQL, un vincolo di chiave esterna può fare riferimento esattamente a una tabella. Il database non può applicare la chiave esterna in base al valore in CommentTypeID.

Potreste essere interessati a verificare il seguente post Stack Overflow per una soluzione per affrontare questo problema:

2

Il primo approccio non è grande, dal momento che è abbastanza denormalizzato. Ogni volta che aggiungi un nuovo tipo di entità, devi aggiornare la tabella. Potrebbe essere meglio renderlo un attributo del documento - I.e. memorizzare il commento in linea nella tabella del documento.

Per l'approccio ResourceID di lavorare con integrità referenziale, è necessario avere un tavolo Resource, e una chiave esterna ResourceID in tutto il vostro documento, progetto ecc .. entità (o utilizzare una tabella di mappatura.) Fare "ResourceID "un tuttofare, che può essere un ID del documento, ID del progetto ecc. non è una buona soluzione poiché non può essere utilizzato per l'indicizzazione sensibile o il vincolo di chiave esterna.

Per normalizzare, è necessario la tabella dei commenti in una tabella per tipo di risorsa.

Comment 
------- 
CommentID 
CommentText 
...etc 

DocumentComment 
--------------- 
DocumentID 
CommentID 

ProjectComment 
-------------- 
ProjectID 
CommentID 

Se è consentito un solo commento, quindi si aggiunge un vincolo univoco sulla chiave esterna per l'entità (DocumentID, ProjectID etc.), garantendo che non ci può essere solo una riga per l'oggetto dato e così solo un commento. Puoi anche assicurarti che i commenti non siano condivisi usando un vincolo univoco su CommentID.

EDIT: È interessante notare che questo è quasi parallelo all'implementazione normalizzata di ResourceID - sostituire "Comment" nel nome della tabella, con "Resource" e modificare "CommentID" in "ResourceID" e si dispone della struttura necessaria per associare un ResourceID con ogni risorsa. È quindi possibile utilizzare una singola tabella "ResourceComment".

Se ci saranno altre entità associate a qualsiasi tipo di risorsa (es. Dettagli di controllo, diritti di accesso, ecc.), Quindi usare le tabelle di mappatura delle risorse è la strada da percorrere, poiché ti consentirà di aggiungere commenti normalizzati e qualsiasi altra entità correlata alle risorse.

+0

Questo non impedisce la condivisione di commenti tra tipi di entità differenti (se questo è un requisito). Un ID commento potrebbe essere sia in DocumentComments sia in ProjectComments –

+1

@Tom: corretto e, per quanto ne so, non esiste un modo relazionale per applicare questo diverso dall'utilizzo di trigger per verificare dopo la modifica, o se disponibile, una vista materializzata che UNION tutte le risorseID dalle tabelle Documento, Progetto ecc. E applica un costrutto di unicità. – mdma

+0

Qualcuno ha pubblicato un link (sembra che l'abbiano rimosso) con una descrizione di come digitare super le tabelle. Non penso che sarebbe appropriato in questo caso, ma qualcosa del genere permetterebbe l'applicazione di cui stiamo parlando. –

0

L'opzione 2 è un buon modo per andare. Il problema che vedo con questo è che stai mettendo la chiave Resiuce su quel tavolo. Ciascuno degli ID delle diverse risorse potrebbe essere duplicato. Quando si uniscono le risorse ai commenti, è più probabile che vengano visualizzati commenti che non appartengono a quel particolare risultato. Questo sarebbe considerato un numero che molti si uniscono. Penso che un'opzione migliore sarebbe avere le tabelle delle risorse, la tabella dei commenti e le tabelle che fanno riferimento al tipo di risorsa e alla tabella dei commenti.

+0

Le mie domande su questo tavolo sarà sempre qualcosa di simile: SELECT * FROM tblComments DOVE CommentTypeID = 1, ResourceID = 1244 – Gazeth

0

Se si trasportano lo stesso tipo di dati su tutti i commenti indipendentemente da ciò su cui sono commenti, voterei contro la creazione di più tabelle di commenti. Forse un commento è solo "cosa riguarda" e il testo, ma se non hai altri dati ora, è probabile che tu: data di inserimento del commento, id utente di chi lo ha creato, ecc. Con più tabelle, tu è necessario ripetere tutte queste definizioni di colonna per ogni tabella.

Come indicato, l'utilizzo di un singolo campo di riferimento indica che non è possibile inserire un vincolo di chiave esterna su di esso. Questo è un peccato, ma non infrange nulla, significa solo che devi fare la validazione con un trigger o nel codice. Più seriamente, i join diventano difficili. Puoi semplicemente dire "da unire il commento al documento usando (documentid)". È necessario un join complesso in base al valore del campo del tipo.

Così mentre i campi di puntatori multipli sono brutti, tendo a pensare che sia la strada giusta da percorrere. So che alcune persone db dicono che non dovrebbe mai esserci un campo nullo in una tabella, che dovresti sempre suddividerlo in un'altra tabella per evitare che ciò accada, ma non vedo alcun vantaggio reale nel seguire questa regola.

Personalmente sarei disponibile ad ascoltare ulteriori discussioni su pro e contro.

3

L'opzione 2 è non una buona soluzione per un database relazionale. Si chiama associazioni polimorfiche (come menzionato da @Daniel Vassallo) e rompe la definizione fondamentale di una relazione.

Ad esempio, si supponga di avere un ResourceId di 1234 su due righe diverse. Questi rappresentano la stessa risorsa? Dipende se CommentTypeId è lo stesso su queste due righe. Ciò viola il concetto di tipo in una relazione. Vedi SQL and Relational Theory di C. J. Date per ulteriori dettagli.

Un altro indizio del fatto che si tratta di un disegno rotto è che non è possibile dichiarare un vincolo di chiave esterna per ResourceId, perché potrebbe puntare a una qualsiasi delle diverse tabelle.Se si tenta di applicare l'integrità referenziale tramite trigger o qualcosa del genere, ci si ritrova a riscrivere il trigger ogni volta che si aggiunge un nuovo tipo di risorsa commentabile.

avrei risolvere questo con la soluzione che @mdma brevemente indicato (ma poi ignora):

CREATE TABLE Commentable (
    ResourceId INT NOT NULL IDENTITY, 
    ResourceType INT NOT NULL, 
    PRIMARY KEY (ResourceId, ResourceType) 
); 

CREATE TABLE Documents (
    ResourceId INT NOT NULL, 
    ResourceType INT NOT NULL CHECK (ResourceType = 1), 
    FOREIGN KEY (ResourceId, ResourceType) REFERENCES Commentable 
); 

CREATE TABLE Projects (
    ResourceId INT NOT NULL, 
    ResourceType INT NOT NULL CHECK (ResourceType = 2), 
    FOREIGN KEY (ResourceId, ResourceType) REFERENCES Commentable 
); 

Ora ogni tipo di risorsa ha la sua tabella, ma la chiave primaria seriale è assegnato in modo univoco da commentabile. Un dato valore di chiave primaria può essere utilizzato solo da un tipo di risorsa.

CREATE TABLE Comments (
    CommentId INT IDENTITY PRIMARY KEY, 
    ResourceId INT NOT NULL, 
    ResourceType INT NOT NULL, 
    FOREIGN KEY (ResourceId, ResourceType) REFERENCES Commentable 
); 

Ora riferimento Commenti Risorse descrittive, con integrità referenziale applicata. Un determinato commento può fare riferimento solo a un tipo di risorsa. Non c'è possibilità di anomalie o ID delle risorse in conflitto.

Ho più informazioni sulle associazioni polimorfiche nella mia presentazione Practical Object-Oriented Models in SQL e sul mio libro SQL Antipatterns.

+1

Complimenti per SQL antipattern, Bill :) che è una grande parte di lavoro. Ho ricevuto l'ebook la scorsa settimana e attualmente sto leggendo un capitolo al giorno. Il mio segnalibro è sul capitolo delle associazioni polimorfe incidentalmente :) ... congratulazioni! –

1

Non andrei con nessuna di queste soluzioni. A seconda alcune delle specifiche delle vostre esigenze si potrebbe andare con una tavola super-tipo:

CREATE TABLE Commentable_Items (
    commentable_item_id INT NOT NULL, 
    CONSTRAINT PK_Commentable_Items PRIMARY KEY CLUSTERED (commentable_item_id)) 
GO 
CREATE TABLE Projects (
    commentable_item_id INT NOT NULL, 
    ... (other project columns) 
    CONSTRAINT PK_Projects PRIMARY KEY CLUSTERED (commentable_item_id)) 
GO 
CREATE TABLE Documents (
    commentable_item_id INT NOT NULL, 
    ... (other document columns) 
    CONSTRAINT PK_Documents PRIMARY KEY CLUSTERED (commentable_item_id)) 
GO 

Se l'ogni elemento può avere un solo commento e commenti non sono condivise (ad esempio un commento può appartenere solo a un'entità) quindi potresti semplicemente inserire i commenti nella tabella Commentable_Items. Altrimenti potresti collegare i commenti fuori da quella tabella con una chiave esterna.

Non mi piace molto questo approccio nel tuo caso specifico, perché "fare commenti" non è abbastanza per mettere insieme gli elementi come quello nella mia mente.

Probabilmente andrei con tabelle Commenti separate (supponendo che tu possa avere più commenti per articolo - altrimenti inseriscili nelle tue tabelle di base). Se un commento può essere condiviso tra più tipi di entità (ad esempio, un documento e un progetto possono condividere lo stesso commento), poi hanno un tavolo centrale Commenti e più tabelle entità-relazione-commento:

CREATE TABLE Comments (
    comment_id INT   NOT NULL, 
    comment_text NVARCHAR(MAX) NOT NULL, 
    CONSTRAINT PK_Comments PRIMARY KEY CLUSTERED (comment_id)) 
GO 
CREATE TABLE Document_Comments (
    document_id INT NOT NULL, 
    comment_id  INT NOT NULL, 
    CONSTRAINT PK_Document_Comments PRIMARY KEY CLUSTERED (document_id, comment_id)) 
GO 
CREATE TABLE Project_Comments (
    project_id  INT NOT NULL, 
    comment_id  INT NOT NULL, 
    CONSTRAINT PK_Project_Comments PRIMARY KEY CLUSTERED (project_id, comment_id)) 
GO 

Se si desidera vincolare commenti a un singolo documento (ad esempio), quindi è possibile aggiungere un indice univoco (o modificare la chiave primaria) sul comment_id all'interno di tale tabella di collegamento.

Sono tutte queste "piccole" decisioni che influiranno su specifici PK e FK. Mi piace questo approccio perché ogni tabella è chiara su quello che è. Nei database che di solito è meglio avere tabelle/soluzioni "generiche".

0

Pawnshop Applicazione:

Ho tabelle separate per il prestito, acquisto, inventario & operazioni di vendita. Ogni tabelle righe sono uniti alle rispettive righe clienti da:

customer.pk [serial] = loan.fk [integer]; 
        = purchase.fk [integer]; 
        = inventory.fk [integer]; 
        = sale.fk [integer]; 

ho codificato le quattro tabelle in una tabella denominata "transazione", dove una colonna:

transazione.trx_type char (1) {L = prestito, P = Acquisto, I = Inventario, S = Vendita}

Scenario:

un cliente inizialmente pedine merce, fa un paio di pagamenti di interessi, poi decide che vuole vendere la merce al banco dei pegni, che poi mette la merce in inventario e alla fine la vende a un altro cliente.

Ho progettato un tavolo un'operazione generica dove per esempio:

transaction.main_amount DECIMAL (7,2)

in un'operazione di prestito tiene la quantità pedina, in un acquisto detiene il prezzo di acquisto, in inventario e vendita detiene il prezzo di vendita.

Questo è chiaramente un design denormalizzato, ma ha reso la programmazione molto più semplice e ha migliorato le prestazioni. Ora è possibile eseguire qualsiasi tipo di transazione da una schermata, senza la necessità di passare a tabelle diverse.