2009-03-17 11 views
6

Biorąc pod uwagę tabelę modeli "A", które mogą mieć wiele modeli potomnych "B", z których "B" będzie mieć jeden lub więcej modeli potomnych "C". Brzmi to proste, ale muszę wymusić, że dla każdego "A", każde "B" musi mieć unikalny zbiór "C". na przykład C nie może być dzieckiem dwóch "B, które są częścią tego samego rodzica" A "... ale" C "może być dzieckiem wielu" B ", zważywszy, że każde" B "rodzica" A "jest odrębne ..Najlepszy projekt schematu dla relacji tabeli, który wymusza integralność

Czy to ma sens, czy powinienem rozwikłać mój scenariusz? Pozdrawiam z góry!

Pamiętaj, że wiem, że ta zasada będzie egzekwowana w aplikacji, ale nie będzie możliwe, aby baza danych była w nieprawidłowym stanie.

Edytuj: cześć wszystkim, fantastyczne opinie, więc najpierw muszę Ci podziękować za podzielenie się ze mną swoją wiedzą.

Wystarczy, aby wyjaśnić sytuację, wytłumaczę scenariusz, ale oto kilka uwag:

„A” ma zero lub więcej „B”, a „B” jest domyślnie powiązany z „A”, i jako taki jest zawsze dzieckiem jednego "A". "C" to w pewnym stopniu element główny, który jest powiązany z wieloma "B" oraz innymi elementami w bazie danych.


Herezje prawdziwa historia:

Jest to strona, która zawiera wiele majtki (A), oraz wielu członków (C), krótka może mieć wiele zgłoszeń (B), którego złożenie wniosku zawsze mają jednego lub więcej powiązanych członków. Chodzi o to, że zgłoszenie może być w istocie kolaboracją, a każdy członek nie ma więcej "władzy" niż jakikolwiek inny, ale będzie istnieć inny system do sprawdzania zasad współpracy członków.

Krótko mówiąc, członek może przesłać tylko jedno zgłoszenie, a zgłoszenie może mieć wielu członków (współpracowników).

Nadzieję, że pomaga, ale myślę, że dałeś mi już dużo pomocy!

Steve.

+0

+1 za to, że DBMS ma egzekwować ograniczenie, niezależnie od tego, co robią aplikacje. –

+0

Jestem z Jonathanem na ten temat, zbyt wiele osób bierze pod uwagę tylko to, że aplikacja wykonuje tę pracę i ma złe dane. – HLGEM

+0

Jak dowodzi moja odpowiedź, twój scenariusz jest dość zaciemniony i jeśli możesz go zmienić, powinieneś. Liczba systemów obsługujących CREATE ASSERTION jest bardzo ograniczona. Instrukcja wymagana do sprawdzenia ograniczeń jest skomplikowana do użycia w wyzwalaczu. –

Odpowiedz

3

myślę, że trzeba SQL standardowych twierdzeń, które są (niestety) w dużej mierze wdrożone przez rzeczywistych DBMS.

wszystkie odpowiedzi są zgadzając się, że istnieją trzy tabele zwane podstawowe TABLEA, TableB i TableC, każdy zawierający własną kolumna ID:

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

nie jest jasne na podstawie poniższego opisu problemu czy dany pojedyncza wartość B może mieć wiele wpisów nadrzędnych A. Oczywiste jest, że pojedynczy C może mieć wiele wpisów nadrzędnych B. Jeśli B jest przywiązany do jednego A, projekt TableB może zostać zmieniona w celu:

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

jeśli B może wiązać się z kilkoma różnymi, wtedy połączenie jest najlepiej reprezentowana przez tabelę łączącą:

A_and_B (A_ID REFERENCES TableA, 
     B_ID REFERENCES TableB, 
     PRIMARY KEY (A_ID, B_ID) 
     ) 

Z opisu nie wynika też, czy litery C związane z literą B muszą być takie same dla każdego A, z którym B jest powiązane, czy też różne A mogą odwoływać się do tego samego B, a zbiór C jest powiązany z B dla A1 może się różnić od zestawu C związanych z B dla A2. (Oczywiście, jeśli pojedynczy B może być powiązany tylko z jednym A, ten problem jest dyskusyjny.)

Dla celów tej odpowiedzi, zakładam, że dowolne B jest powiązane z pojedynczym A, więc struktura TableB zawiera A_ID jako klucz obcy. Ponieważ pojedynczy C może być powiązany z wieloma B, odpowiednia struktura jest nowa tabela łączenia:

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

Uproszczenie (pomijając zasady dotyczące deferrability i bezpośredniości) twierdzenie wygląda następująco:

CREATE ASSERTION assertion_name CHECK (<search_condition>) 

Tak , kiedy już mamy zestaw decyzji projektowych, możemy napisać potwierdzenie do sprawdzenia poprawności danych.Biorąc pod uwagę, stoły TABLEA, TableB (z kluczem A_ID obcej), TableC i B_and_C wymóg jest taki, że liczba wystąpień danego C_ID całej kompletnej A jest 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 
    ) 
) 

[Zmieniony: Myślę, że to jest bardziej precyzyjne:

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

]

zestaw dołączyć warunki zmienia się wraz z innymi przepisami na jak tabele są połączone, ale ogólna struktura ograniczeniem pozostaje ten sam - nie musi istnieć więcej niż jeden R odniesienie do danego C_ID dla określonego A_ID.


w komentarzach poniżej, meandmycode Uwagi:

mam wrażenie, że nie jest to wada w moim projekcie. Moją prawdziwą logiką jest to, że "B" zawsze ma co najmniej jedno dziecko "C". Nie ma to sensu, ponieważ "B" musi istnieć, zanim będzie można przywiązać dziecko. Baza danych obecnie zezwala na dołączenie "B" do "A" bez posiadania co najmniej JEDNEGO "C" .. dziecko, ja jako taki zmieniam "B" tak, że ma ono pole, które odnosi się do jego pierwotne dziecko "C", a także dziecko z dodatkowymi "C", ale teraz mam kolekcję, która może również zawierać pierwotne "C" określone przez "B", co byłoby ... błędne.

Czy istnieje wzór bazy danych, który wskazywałby zasadę "jeden lub więcej dzieci", a nie zero lub więcej?

Myślę, że masz problemy z modelem. Trudno jest utworzyć B, jeśli musi już istnieć C, który odnosi się do nowo utworzonego B, zwłaszcza jeśli C musi odnosić się tylko do istniejących B. Na myśl przychodzi zwrot "kurczak i jajko". Zwykle pozwalasz, aby B miały zero lub więcej C w takim kontekście.

Wciąż nie ustalono, czy tabela B ma klucz obcy A_ID, czy też ma tabelę łączącą, taką jak A_i_B. Jeśli ma klucz obcy, to prawdopodobnie nie możesz utworzyć B, dopóki nie stworzysz A, do którego się odnosi.

Nie sądzę, aby jeden C ID w tabeli B był dobrym pomysłem - powoduje to asymetryczne przetwarzanie (trudniejszy SQL). Oznacza to również, że jeśli chcesz usunąć ten C, musisz zaktualizować rzeczy tak, aby jedno z pozostałych odwołań C zostało usunięte z tabeli, w której się aktualnie znajduje, a następnie zaktualizować wartość w rekordzie B. To niechlujne, być grzecznym.

Myślę, że trzeba poprawić swoje pytanie, aby zdefiniować rzeczywistą strukturę tabeli, którą oglądasz - zgodnie z liniami przedstawionymi w różnych odpowiedziach; możesz użyć potrójnych kropek do przedstawienia innych, ale nieistotnych kolumn. Twierdzenie, które zasugerowałem, prawdopodobnie musiałoby zostać zaimplementowane jako pewien rodzaj wyzwalacza - który dostaje się do notacji specyficznej dla systemu DBMS.


Od zmienionym opisem majtek (A), zgłoszeń (B) i członków (C), jest oczywiste, że pojedyncza złożenie dotyczy tylko jeden krótki, tak, że wnioski mogą mieć prosty klucz obcy że określa brief, dla którego jest składany. Członek może współpracować tylko przy jednym zgłoszeniu na dany brief. Zostanie wyświetlona tabela "submission_collaborators" z kolumnami do identyfikacji zgłoszenia i członka, kombinacja jest kluczem podstawowym, a każda kolumna jest kluczem obcym.

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

Stąd, wymaga się, aby po musi zwrócić nie rzędach

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 

To samo zapytanie, że osadzony na stwierdzeniu stworzyć (drugi wariant). Możesz również uzyskać dodatkowe informacje (krótki tytuł, tytuł zgłoszenia, nazwę członka, różne daty itp.), Ale sedno problemu polega na tym, że przedstawione zapytanie nie może zwracać żadnych danych.

+0

Jonathan, dzięki za odpowiedź, jest bardzo zwięzły .. Mam wrażenie, że jest wada mojego projektu, moja prawdziwa logika jest taka, że ​​"B" zawsze ma co najmniej jedno dziecko "C" ... to nie mieć sens, biorąc pod uwagę, że "B" musi istnieć, zanim będzie można przywiązać dziecko. * cont * – meandmycode

+0

Baza danych obecnie zezwala na dołączenie "B" do "A" bez posiadania co najmniej JEDNEGO "C" .. dziecka, ja jako taki zmienię "B" tak, że ma pole odnosi się do swojego pierwotnego dziecka "C", a także posiada kolekcję dziecięcą * c * – meandmycode

+0

dodatkowych "C", ale teraz mam kolekcję, która może również zawierać pierwotne "C" określone przez "B", co być w błędzie – meandmycode

0

Dodaj identyfikator tabeli TableA do tabeli B i dodaj go do klucza podstawowego, i wykonaj to samo dla TableB i TableC.

edit:

Uważam, że pierwsza część tej odpowiedzi będzie działać na A do punktu B ograniczeń. Chciałbym jednak umieścić tabelę łączącą między B i C, która również posiadała PK. w ten sposób masz 1: N między A: B, a twoje ograniczenia są następnie egzekwowane.

+0

To przyniesie pracę, ponieważ C może być dzieckiem wielu B, o ile nie mają one wspólnego z A. –

+0

@Gamecat - The powód, dla którego dałeś, że nie działa, jest dokładnie tym pytaniem, które próbujemy rozwiązać, a ta odpowiedź jest błędna, ale dzieje się tak, ponieważ nic nie powstrzymuje wszystkich B1 przed posiadaniem C1 jako dziecka. – cdeszaq

-1

Nie sądzę, że będziesz w stanie to zrobić z prostymi deklaratywnymi więzami integralności referencyjnej. Najlepszym sposobem wymuszenia logiki może być użycie wyzwalaczy do implementacji ograniczeń biznesowych i wycofanie wszelkich wstawek lub aktualizacji, które naruszają reguły.

+0

To naprawdę można zrobić. Zobacz poniżej. Wyzwalacze po prostu zamoczone w wodzie i powodują problemy, jeśli nie są dobrze udokumentowane. – cdeszaq

+0

@cdeszaq: który DBMS zrobiłbyś to - i jak zrobiłbyś to bez wyzwalacza? –

0

To, co masz, to międzyplatformowy związek . To, co musisz zrobić, to mieć tabelę, która łączy A i B i C razem w kluczu podstawowym. Ponieważ klucze podstawowe nie mogą być duplikowane, będzie to wymagało tylko jednego C dla każdego A, a także każdego B. Stworzy to unikalną kolekcję, której szukasz.

uzyskać następującą strukturę tabeli:

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

głównych kluczy są w szelki, klucze obce są w nawiasach.

Sprawdzić numer here, aby uzyskać więcej informacji.

+0

Jak to wymusza "C nie może być dzieckiem dwóch" B, które są częścią tego samego rodzica "A" "? – Jon

+0

To nie jest wystarczająco restrykcyjne: pozwala na {A = 1, B = 1, C = 1} i {A = 1, B = 2, C = 1}, ale reguły mówią, że to niedozwolone. –

3

Myślę, że udało mi się zdobyć twój model relacji; Jeśli nie to, że głosowania na unobfuscation:

  • A [{AID}, ...]
  • B [{BID} AID, ...]
  • C [{CID} .. .]
  • B_C_Link [{BID CID} AID]
    • dodatkowe indeks unikalny (AID CID)

Zapis wykorzystuje wskaźnik {} klucza podstawowego. Ponieważ As może mieć wiele Bs (przez umieszczenie AID na B), Bs może mieć Cs (używając tabeli B_C_Link dla wielu do wielu), a wiele Cs nie może należeć do tego samego A (przez dodanie AID do wielu do wielu tabeli i egzekwowania (AID, CID) wyjątkowości.

+0

Masz nadmiarowość w tabeli B_C_Link. AID można znaleźć na podstawie wartości BID, więc twoja tabela nie znajduje się w BCNF. W rzeczywistości, myślę, że jest to tylko w 1NF, nawet 2NF, ponieważ BID -> AID jest zależnością przechodnią. Jeśli zaakceptujesz tę nadmiarowość, dodatkowy unikatowy indeks działa stosunkowo czysto. –

+0

Prawidłowo, wymaga to denormalizacji bazy danych. Jak mówią: "Normalizuj, aż to boli, denormalizuj, aż zadziała". –

Powiązane problemy