W mojej bazie danych mam 3 odpowiednie tabele.
CREATE TABLE dbo.Group
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.User
(
ID int NOT NULL,
Name varchar(50) NOT NULL
)
CREATE TABLE dbo.Ticket
(
ID int NOT NULL,
Owner int NOT NULL,
Subject varchar(50) NULL
)
Użytkownicy należą do wielu grup. Odbywa się to poprzez relację wiele do wielu, ale w tym przypadku nie ma to znaczenia. Bilet może być własnością grupy lub użytkownika za pośrednictwem pola dbo.Ticket.Owner.
Jaki byłby NAJPOPRAWNIEJSZY sposób opisania relacji między biletem a opcjonalnie użytkownikiem lub grupą?
Myślę, że powinienem dodać flagę w tabeli biletów, która mówi, jaki typ go posiada.
sql-server
relational-database
Darthg8r
źródło
źródło
Odpowiedzi:
Masz kilka opcji, z których wszystkie różnią się „poprawnością” i łatwością użycia. Jak zawsze, właściwy projekt zależy od Twoich potrzeb.
Możesz po prostu utworzyć dwie kolumny w Ticket, OwnedByUserId i OwnedByGroupId i przypisać do każdej tabeli klucze obce dopuszczające wartość null.
Można utworzyć tabele referencyjne M: M, które umożliwią zarówno relację bilet: użytkownik, jak i bilet: grupa. Być może w przyszłości zechcesz zezwolić, aby jeden bilet był własnością wielu użytkowników lub grup? Ten projekt nie narzuca, że bilet musi być własnością tylko jednej jednostki.
Możesz utworzyć domyślną grupę dla każdego użytkownika i mieć bilety po prostu należące do prawdziwej grupy lub domyślnej grupy użytkownika.
Lub (mój wybór) modeluj jednostkę, która działa jako baza zarówno dla użytkowników, jak i grup, i ma bilety należące do tej jednostki.
Oto przybliżony przykład użycia opublikowanego schematu:
źródło
SELECT t.Subject AS ticketSubject, CASE WHEN u.Name IS NOT NULL THEN u.Name ELSE g.Name END AS ticketOwnerName FROM Ticket t INNER JOIN Party p ON t.Owner=p.PartyId LEFT OUTER JOIN User u ON u.ID=p.PartyId LEFT OUTER JOIN Group g on g.ID=p.PartyID;
do tego, że w rezultacie miałbyś każdy temat biletu i nazwę właściciela.Pierwsza opcja na liście @Nathana Skerla jest tym, co zostało zaimplementowane w projekcie, z którym kiedyś pracowałem, w którym podobna relacja została ustalona między trzema tabelami. (Jeden z nich odnosił się do dwóch innych, pojedynczo).
Tak więc tabela odwołująca się miała dwie kolumny klucza obcego, a także miała ograniczenie gwarantujące, że dokładnie jedna tabela (nie obie, nie żadna) była przywoływana przez pojedynczy wiersz.
Oto, jak może wyglądać po zastosowaniu do tabel:
Jak widać,
Ticket
tabela ma dwie kolumnyOwnerGroup
iOwnerUser
obie są kluczami obcymi dopuszczającymi wartość null. (Odpowiednie kolumny w pozostałych dwóch tabelach są odpowiednio tworzone jako klucze podstawowe.) OgraniczenieCK_Ticket_GroupUser
sprawdzające zapewnia, że tylko jedna z dwóch kolumn klucza obcego zawiera odwołanie (druga ma wartość NULL, dlatego obie muszą mieć wartość null).(Włączony klucz główny
Ticket.ID
nie jest konieczny dla tej konkretnej implementacji, ale zdecydowanie nie zaszkodzi mieć go w takiej tabeli).źródło
RefID
,RefType
gdzieRefType
jest ustalonym identyfikatorem tabeli docelowej. Jeśli potrzebujesz integralności, możesz sprawdzić w wyzwalaczu lub warstwie aplikacji. W takim przypadku możliwe jest pobieranie ogólne. SQL powinien zezwalać na taką definicję FK, ułatwiając nam życie.Jeszcze inną opcją jest umieszczenie w
Ticket
jednej kolumnie określającej typ jednostki będącej właścicielem (User
lubGroup
), drugiej kolumny z odniesieniemUser
lubGroup
identyfikatorem identyfikatorem i NIE używać kluczy obcych, ale zamiast tego polegać na wyzwalaczu, aby wymusić integralność referencyjną.Dwie zalety, które widzę tutaj w porównaniu z doskonałym modelem Nathana (powyżej):
źródło
Innym podejściem jest utworzenie tabeli asocjacji zawierającej kolumny dla każdego potencjalnego typu zasobu. W twoim przykładzie każdy z dwóch istniejących typów właścicieli ma własną tabelę (co oznacza, że masz coś do odniesienia). Jeśli tak będzie zawsze, możesz mieć coś takiego:
Dzięki temu rozwiązaniu można kontynuować dodawanie nowych kolumn podczas dodawania nowych jednostek do bazy danych, a także usuwać i odtwarzać wzorzec ograniczenia klucza obcego pokazany przez @Nathan Skerl. To rozwiązanie jest bardzo podobne do @Nathana Skerla, ale wygląda inaczej (w zależności od preferencji).
Jeśli nie zamierzasz mieć nowej tabeli dla każdego nowego typu właściciela, być może byłoby dobrze dołączyć kolumnę owner_type zamiast kolumny klucza obcego dla każdego potencjalnego właściciela:
Dzięki powyższej metodzie możesz dodać dowolną liczbę typów właścicieli. Owner_ID nie miałby ograniczenia klucza obcego, ale byłby używany jako odniesienie do innych tabel. Wadą jest to, że musiałbyś spojrzeć na tabelę, aby zobaczyć, jakie są typy właścicieli, ponieważ nie jest to od razu oczywiste na podstawie schematu. Sugerowałbym to tylko wtedy, gdy nie znasz wcześniej typów właścicieli i nie będą one łączyły się z innymi tabelami. Jeśli znasz wcześniej typy właściciela, wybrałbym rozwiązanie takie jak @Nathan Skerl.
Przepraszam, jeśli źle napisałem SQL, po prostu wrzuciłem to razem.
źródło
Myślę, że byłby to najbardziej ogólny sposób przedstawienia tego, czego chcesz, zamiast używania flagi.
źródło