8

tylko próbuje dowiedzieć się, że najlepszym sposobem, aby zaprojektować tabelę następujący scenariusz:Generic tabeli bazy danych projekt

Mam kilka obszarów, w moim systemie (dokumenty, projekty, grup i klientów), a każdy z nich może mieć komentarze zostały zarejestrowane przeciwko nim.

Moje pytanie powinienem mieć jeden stół tak:

CommentID 
DocumentID 
ProjectID 
GroupID 
ClientID 
etc 

Jeżeli tylko jeden z identyfikatorów będzie miał dane, a reszta będzie NULL lub powinien mieć osobną tabelę CommentType i mają moje komentarze stół w ten sposób:

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

Uważam, że opcja 2 byłaby bardziej wydajna z punktu widzenia indeksowania. Czy to jest poprawne?

+0

Czy można skomentować jeden komentarz dla więcej niż jednego przedmiotu? Na przykład dokument i projekt lub dwa dokumenty? –

+0

Żaden komentarz nie może dotyczyć tylko jednego przedmiotu. – Gazeth

Odpowiedz

0

z opcji dajesz, chciałbym przejść do numeru 2.

3

Z perspektywy klucza obcego, pierwszy przykład jest lepiej, bo można mieć wiele kluczy obcych na kolumnie ale dane musi istnieć wszystkie te odniesienia. Jest także bardziej elastyczny, jeśli zmieniają się reguły biznesowe.

+0

+1 ... Dobry punkt w problemie kluczy obcych. –

+1

Nie tylko jest to problem z egzekwowaniem kluczy obcych, ale również odzyskanie wszystkich danych, musisz dołączyć do tej samej tabeli cztery razy. Mogą również mieć cztery osobne tablice komentarzy, które są wyspecjalizowane jako użyteczne, ponieważ przynajmniej możesz użyć ograniczeń FK. Tabele EAV w ogóle nie są dobrze skalowane. – HLGEM

5

Zapoznaj się z normalizacją bazy danych.

Null w sposób, w jaki opisujesz, będzie dużą oznaką, że baza danych nie została zaprojektowana prawidłowo.

Musisz rozdzielić wszystkie swoje tabele tak, aby dane w nich zawarte były w pełni znormalizowane, co pozwoli Ci zaoszczędzić sporo czasu w dalszej kolejności, a to jest o wiele lepsze ćwiczenie, aby wejść w nawyk.

+4

Jestem pewien, że poprawnie pisałem "normalizację", jestem w Wielkiej Brytanii. –

+1

Przepraszam :) Poprawiałem kilka literówek i mój moduł sprawdzania pisowni ich podświetlił. Pomyślałem, że tak być może, ale pomyślałem, że może pomogę ci, Brytyjczycy, zobaczą błądzący sposób;) –

3

Aby kontynuować od @OMG Ponies' answer, co można opisać w drugim przykładzie nazywa się polimorficznych Stowarzyszenie, gdzie klucz obcy ResourceID może odwołać wiersze w więcej niż jednym stole. Jednak w bazach danych SQL ograniczenie klucza obcego może odnosić się tylko do jednej tabeli. Baza danych nie może wymusić klucza obcego zgodnie z wartością w CommentTypeID.

Możesz być zainteresowany w sprawdzeniu następujące przepełnieniem stosu stanowisko dla jednego rozwiązania do rozwiązania tego problemu:

2

Pierwsze podejście nie jest wielki, ponieważ jest dość nieznormalizowana. Za każdym razem, gdy dodajesz nowy typ jednostki, musisz zaktualizować tabelę. Być może lepiej będzie, jeśli to będzie atrybut dokumentu - tj. przechowuj komentarz w tekście w tabeli dokumentów.

dla podejścia ResourceID pracować z więzów integralności, trzeba będzie mieć Resource stół, a ResourceID klucz obcy w całym dokumencie, Project etc .. podmioty (lub użyć tabeli mapowania.) Dokonywanie „RESOURCEID "klucz wszystkich transakcji, który może być identyfikatorem dokumentu, identyfikatorem projektu itp., nie jest dobrym rozwiązaniem, ponieważ nie można go użyć do sensownego indeksowania lub ograniczenia klucza obcego.

Aby znormalizować, musisz utworzyć tabelę komentarzy w jednej tabeli dla każdego typu zasobu.

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

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

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

Jeśli tylko jeden komentarz jest dozwolone, a następnie dodać unikalny ograniczenie na klucz obcy dla podmiotu (DocumentID, ProjectID itd.) Gwarantuje to, że może być tylko jeden wiersz dla danego punktu, a więc tylko jeden komentarz. Możesz także zagwarantować, że komentarze nie będą współużytkowane przez użycie wyjątkowego ograniczenia dla elementu CommentID.

EDYCJA: Co ciekawe, jest to prawie równorzędne ze znormalizowaną implementacją ResourceID - zastąp "Comment" w nazwie tabeli, z "Resource" i zmień "CommentID" na "ResourceID" i masz strukturę potrzebną do powiązania ResourceID dla każdego zasobu. Następnie można użyć pojedynczej tabeli "ResourceComment".

Jeśli pojawią się inne podmioty powiązane z dowolnym rodzajem zasobu (np. Szczegóły dotyczące audytu, prawa dostępu itp.), To najlepszym rozwiązaniem jest użycie tabel odwzorowania zasobów. dodawać znormalizowane komentarze i wszelkie inne jednostki powiązane z zasobami.

+0

To jednak nie zapobiega dzieleniu się komentarzami pomiędzy różnymi typami jednostek (jeśli jest to wymagane). Identyfikator komentarza może znajdować się zarówno w Elementach Dokumentu, jak i Elementach Projektu –

+1

@Tom - Zgadza się, i o ile mi wiadomo, nie ma żadnego relacyjnego sposobu na wymuszenie tego innego niż przez użycie wyzwalaczy do sprawdzenia po modyfikacji lub jeśli jest dostępny, zmaterializowanego widoku, który UNIONs wszystkie identyfikatory resourceID z tabeli Document, Project itd. I stosuje kryterium wyjątkowości. – mdma

+0

Ktoś opublikował link (wygląda na to, że go usunął) z opisem, jak super pisać tabele. Nie sądzę, że byłoby to odpowiednie w tym przypadku, ale coś takiego pozwoliłoby na egzekwowanie, o którym mówimy. –

0

Opcja 2 to dobra droga. Problem, który widzę z tym, polega na umieszczeniu klucza resouce na tym stole. Każdy z identyfikatorów z różnych zasobów może zostać zduplikowany. Kiedy dołączasz zasoby do komentarzy, najprawdopodobniej wymyślisz komentarze, które nie należą do tego konkretnego zasobu. Byłoby to uważane za wielu dla wielu. Myślę, że lepszym rozwiązaniem byłoby posiadanie tabel zasobów, tabeli komentarzy, a następnie tabel, które odwołują się do typu zasobu i tabeli komentarzy.

+0

Moi zapytań na tej tabeli zawsze będzie coś takiego: SELECT * FROM tblComments GDZIE CommentTypeID = 1 i RESOURCEID = 1244 – Gazeth

0

Jeśli masz takie same dane o wszystkich komentarzach, niezależnie od tego, o czym są one komentowane, zagłosuję przeciwko tworzeniu wielu tabel komentarzy. Być może komentarz jest po prostu "rzeczą o czymś" i tekstem, ale jeśli nie masz teraz innych danych, prawdopodobnie: data wpisania komentarza, identyfikator użytkownika osoby, która go utworzyła itd. Z wieloma tabelami muszą powtarzać wszystkie te definicje kolumn dla każdej tabeli.

Jak wspomniano, użycie pojedynczego pola referencyjnego oznacza, że ​​nie można na nim umieścić ograniczenia klucza obcego. Jest to zbyt złe, ale niczego nie psuje, oznacza to tylko, że musisz przeprowadzić walidację za pomocą wyzwalacza lub kodu. Bardziej poważnie, łączenia stają się trudne. Możesz po prostu powiedzieć "z komentarza dołączyć dokument za pomocą (documentid)". Potrzebujesz złożonego sprzężenia na podstawie wartości pola typu.

Podczas gdy pola wielu wskaźników są brzydkie, uważam, że jest to właściwa droga. Wiem, że niektórzy pracownicy db twierdzą, że nigdy nie powinno być pustego pola w tabeli, że zawsze należy je rozbić na inną tabelę, aby temu zapobiec, ale nie widzę żadnej realnej korzyści z przestrzegania tej zasady.

Osobiście byłbym otwarty na dalsze dyskusje na temat zalet i wad.

3

Opcja 2 to nie dobre rozwiązanie dla relacyjnej bazy danych. To się nazywa polimorficzne stowarzyszenia (jak wspomniał @ Daniel Vassallo) i łamie podstawową definicję relacji.

Załóżmy na przykład, że masz ResourceId z 1234 na dwóch różnych wierszach. Czy reprezentują one ten sam zasób? Zależy to od tego, czy CommentTypeId jest taki sam w tych dwóch wierszach. Narusza to koncepcję typu typu w relacji. Zobacz SQL and Relational Theory autorstwa C. J. Data, aby uzyskać więcej informacji.

Inną wskazówką, że jest to zepsuty projekt, to że nie można zadeklarować ograniczenia klucza obcego dla ResourceId, ponieważ może to wskazywać na jedną z kilku tabel.Jeśli próbujesz wymusić integralność referencyjną za pomocą wyzwalaczy lub czegoś podobnego, możesz przepisać wyzwalacz za każdym razem, gdy dodasz nowy typ zasobu z możliwością komentowania.

chciałbym rozwiązać ten problem z rozwiązaniem, które @mdma krótko wspomina (ale wtedy ignoruje):

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 
); 

Teraz każdy typ zasobu ma swój własny stolik, ale seryjny klucz podstawowy jest alokowana jednoznacznie przez Commentable. Dana wartość klucza podstawowego może być używana tylko przez jeden typ zasobu.

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

Odsyłacze do uwag Zweryfikowane zasoby, z wprowadzoną integralnością referencyjną. Dany komentarz może odnosić się tylko do jednego typu zasobu. Nie ma możliwości wystąpienia anomalii lub sprzecznych identyfikatorów zasobów.

Omówię więcej o stowarzyszeniach polimorficznych w mojej prezentacji Practical Object-Oriented Models in SQL i mojej książce SQL Antipatterns.

+1

Kudos dla SQL Antipatterns Bill :) To jest wielki kawałek pracy. W zeszłym tygodniu dostałem e-book, a obecnie czytam jeden rozdział dziennie. Moja zakładka przypadkowo znajduje się w rozdziale o stowarzyszeniach polimorficznych :) ... gratulacje! –

1

Nie poprzestałbym na żadnym z tych rozwiązań. W zależności od niektórych specyfiki wymagań można przejść ze stołem super-type:

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 

Jeżeli każdy element może mieć tylko jeden komentarz i komentarze nie są udostępniane (tj komentarz może należeć tylko do jednego podmiotu) możesz wtedy umieścić komentarze w tabeli Commentable_Items. W przeciwnym razie możesz połączyć komentarze z tej tabeli z kluczem obcym.

Bardzo nie podoba mi się to podejście w konkretnym przypadku, ponieważ "posiadanie komentarzy" nie jest wystarczające, aby umieścić razem elementy w moim umyśle.

Prawdopodobnie pójdę z oddzielnymi tabelami komentarzy (zakładając, że możesz mieć wiele komentarzy na pozycję - inaczej po prostu umieść je w tabelach podstawowych). Jeśli komentarz może być dzielony pomiędzy wielu typów jednostek (tj dokument i projekt mogą dzielić ten sam komentarz), a następnie mieć centralne Komentarze stół i kilka podmiot-komentarz tabele zależność:

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 

Jeśli chcesz ograniczyć komentarze do pojedynczego dokumentu (na przykład), możesz dodać unikalny indeks (lub zmienić klucz podstawowy) w polu comment_id w tej tabeli łączenia.

To wszystkie te "małe" decyzje, które wpłyną na określone PK i FK. Podoba mi się to podejście, ponieważ każda tabela jest jasna co to jest. W bazach danych, które zwykle są lepsze niż posiadanie "ogólnych" tabel/rozwiązań.

0

lombardu Zastosowanie:

mam oddzielne tabele dla kredytu, zakupu, wyposażenie & transakcji sprzedaży. Każdy tabele rzędy są połączone ze swoich wierszy klienta przez:

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

Mam skonsolidowane cztery stoły do ​​jednej tabeli o nazwie „transakcja”, gdzie kolumna:

transakcji.trx_type char (1) {L = Pożyczka, P = Zakup, I = Inventory, S = Sprzedaż}

Scenariusz:

klient początkowo piony towar sprawia, że ​​kilka wypłat odsetek, a następnie decyduje, że chce sprzedać towar lombardowi, który następnie umieszcza towar w magazynie i ostatecznie sprzedaje go innemu klientowi.

Zaprojektowałem rodzajowe tabeli transakcji, gdzie na przykład:

transaction.main_amount dziesiętnych (7,2)

w transakcji kredytowej utrzymuje kwoty Pożyczki, w zakupie trzyma cenę zakupu, w inwentarzu i sprzedaży trzyma cenę sprzedaży.

Jest to oczywiście projekt zdenormalizowany, ale znacznie ułatwił programowanie i zwiększył wydajność. Każdy rodzaj transakcji można teraz wykonać z poziomu jednego ekranu, bez konieczności zmiany na różne tabele.