Jak uniknąć cyklicznej zależności (odwołanie cykliczne) między 3 tabelami?

10

Mam 3 stoły:

  • Ludzie
  • Poczta
  • Lubi

Kiedy projektuję model ER, ma on cykliczną zależność:

         1: N
Ludzie -------- <Post

         1: N
Opublikuj ---------- <Polubienia

         1: N
Ludzie -------- <Lubi

Logika jest następująca:

  • 1 osoba może mieć wiele postów.

  • 1 post ma wiele polubień.

  • 1 osoba może polubić wiele postów (utworzona osoba nie może polubić własnego postu).

Jak mogę usunąć tego rodzaju cykliczny projekt? Czy mój projekt db jest nieprawidłowy?

Ragu
źródło

Odpowiedzi:

10

Zasady biznesowe

Pozwól nam zrobić kilka zmian w przepisach biznesowych, które przedstawiłeś:

  • A Persontworzy zero-jeden-lub-wiele Posts .
  • A Postotrzymuje zero-jeden-lub-wiele Likes .
  • A Personmanifestuje zero-jeden-lub-wiele Likes , z których każdy dotyczy jednego określonego Post .

Modele logiczne

Następnie z takiego zestawu twierdzeń wyprowadziłem dwa modele danych IDEF1X [1] na poziomie logicznym , które pokazano na rysunku 1 .

Rysunek 1 - Modele danych osób i postów

Opcja A

Jak widać w modelu opcją, PersonId migruje [2] od Persondo Postjako klucz obcy (FK), ale otrzymuje nazwę roli [3] o AuthorId, a ta cecha sprawia, że się razem z PostNumber, klucz podstawowy (PK) z The Posttypu jednostki.

Przypuszczam, że Likemoże istnieć tylko w połączeniu z konkretnym Post, więc muszę założyć LikePK, który składa się z trzech różnych atrybutów: PostAuthorId, PostNumberi LikerId. Kombinacja PostAuthorIdi PostNumberjest FK, który stanowi właściwe odniesienie do PostPK. LikerIdjest z kolei FK, który ustanawia odpowiednie powiązanie z Person.PersonId.

Za pomocą tej struktury zapewniasz, że określona osoba może zamanifestować tylko jedno Likewystąpienie w tej samej Postinstancji.

Metody zapobiegające polubieniu przez autora postu własnego posta

Ponieważ nie chcesz dopuszczać możliwości, że dana osoba może polubić swoje posty, na etapie wdrażania powinieneś ustanowić metodę, która będzie porównywać wartość Like.PostAuthorIdz wartością Like.LikerIdkażdej próby INSERT. Jeśli wymienione wartości są zgodne, (a) odrzucasz wstawianie, jeśli nie pasują (b) , pozwalasz na kontynuowanie procesu.

Aby wykonać to zadanie w bazie danych, możesz skorzystać z:

  1. Ograniczenie sprawdzające ale, oczywiście, metoda ta nie obejmuje MySQL, ponieważ nie został wdrożony w tej platformie tak daleko, jak można zobaczyć tutaj i tutaj .

  2. Wiersze kodu w transakcji ACID .

  3. Wiersze kodu w module TRIGGER , które mogą zwrócić niestandardowy komunikat wskazujący na próbę naruszenia reguły.

Opcja B

Jeśli autor nie jest atrybutem, który głównie identyfikuje post w Twojej domenie biznesowej, możesz użyć struktury podobnej do tej przedstawionej w Opcji B.

Takie podejście zapewnia również, że post może polubić tylko ta sama osoba za jednym razem.


Notatki

1. Definicja integracji dla modelowania informacji ( IDEF1X ) jest wysoce godną polecenia techniką modelowania danych, która została zdefiniowana jako standard w grudniu 1993 r. Przez Narodowy Instytut Norm i Technologii Stanów Zjednoczonych ( NIST ).

2. IDEF1X definiuje migrację klucza jako „Proces modelowania polegający na umieszczeniu klucza podstawowego elementu nadrzędnego lub podmiotu ogólnego w jego elemencie podrzędnym lub encji jako klucza obcego”.

3. Nazwa roli to oznaczenie przypisane do atrybutu klucza obcego w celu wyrażenia znaczenia tego atrybutu w kontekście odpowiadającego mu typu jednostki. Nazywanie ról jest zalecane od 1970 r. Przez dr EF Codda w jego najważniejszym artykule zatytułowanym „Relacyjny model danych dla dużych banków wspólnych danych” . Ze swojej strony IDEF1X - zachowanie wierności w zakresie praktyk relacyjnych - również zaleca tę procedurę.

MDCCL
źródło
6

Nie widzę tu nic cyklicznego. Pomiędzy tymi podmiotami są ludzie i posty oraz dwa niezależne relacje. Postrzegałbym polubienia jako realizację jednego z tych związków.

  • Osoba może napisać wiele postów, post jest napisany przez jedną osobę: 1:n
  • Osoba może jak wiele postów, post może być lubiany przez wielu ludzi: n:m
    n: m relacje mogą być realizowane z inną zależnością likes.

Podstawowe wdrożenie

Podstawowa implementacja może wyglądać tak w PostgreSQL :

CREATE TABLE person (
  person_id serial PRIMARY KEY
, person    text NOT NULL
);

CREATE TABLE post (
  post_id   serial PRIMARY KEY
, author_id int NOT NULL  -- cannot be anonymous
     REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE  -- 1:n relationship
, post      text NOT NULL
);

CREATE TABLE likes (  -- n:m relationship
  person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE
, post_id   int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE
, PRIMARY KEY (post_id, person_id)
);

W szczególności pamiętaj, że post musi mieć autora ( NOT NULL), a istnienie polubień jest opcjonalne. Dla istniejących lubi, jednak posti person musi być zarówno odniesienia (egzekwowane przez PRIMARY KEYsprawia, że obie kolumny NOT NULLautomatycznie (można dodać te ograniczenia wyraźnie, nadmiarowo) tak anonimowe sympatie są również niemożliwe.

Szczegóły implementacji n: m:

Zapobiegaj samolubstwu

Napisałeś także:

(utworzona osoba nie może polubić własnego posta).

Nie jest to jeszcze wymuszone w powyższej implementacji. Możesz użyć wyzwalacza .
Lub jedno z tych szybszych / bardziej niezawodnych rozwiązań:

Solidny za cenę

Jeśli musi być solidne , można przedłużyć z FK likesdo postcelu obejmują author_idnadmiarowo. Następnie możesz wykluczyć kazirodztwo za pomocą prostego CHECKograniczenia.

CREATE TABLE likes (
  person_id int REFERENCES person ON UPDATE CASCADE ON DELETE CASCADE
, post_id   int 
, author_id int NOT NULL
, CONSTRAINT likes_pkey PRIMARY KEY (post_id, person_id)
, CONSTRAINT likes_post_fkey FOREIGN KEY (author_id, post_id)
     REFERENCES post(author_id, post_id) ON UPDATE CASCADE ON DELETE CASCADE
, CONSTRAINT no_self_like CHECK (person_id <> author_id)
);

To wymaga się inaczej także zbędne UNIQUEograniczenia w post:

ALTER TABLE post ADD CONSTRAINT post_for_fk_uni UNIQUE (author_id, post_id);

Stawiam na author_idpierwszym miejscu, aby podać użyteczny indeks , będąc przy nim.

Powiązana odpowiedź z więcej:

Tańsze z CHECKograniczeniem

Opierając się na „Podstawowej implementacji” powyżej.

CHECKograniczenia mają być niezmienne. Odwoływanie się do innych tabel w celu sprawdzenia nigdy nie jest niezmienne, tutaj trochę nadużywamy tej koncepcji. Proponuję zadeklarować ograniczenie, NOT VALIDaby właściwie to odzwierciedlić. Detale:

W CHECKtym konkretnym przypadku ograniczenie wydaje się rozsądne, ponieważ autor postu wydaje się atrybutem, który nigdy się nie zmienia. Aby mieć pewność, nie zezwalaj na aktualizacje tego pola.

Mamy fałszyweIMMUTABLE funkcja:

CREATE OR REPLACE FUNCTION f_author_id_of_post(_post_id int)
  RETURNS int AS
'SELECT p.author_id FROM public.post p WHERE p.post_id = $1'
LANGUAGE sql IMMUTABLE;

Zamień „public” na rzeczywisty schemat swoich tabel.
Użyj tej funkcji jako CHECKograniczenia:

ALTER TABLE likes ADD CONSTRAINT no_self_like_chk
   CHECK (f_author_id_of_post(post_id) <> person_id) NOT VALID;
Erwin Brandstetter
źródło
4

Myślę, że masz trudności z ustaleniem tego ze względu na sposób, w jaki określasz reguły biznesowe.

Ludzie i posty to „obiekty”. To jest czasownik.

Masz naprawdę tylko 2 akcje:

  1. Osoba może utworzyć jeden lub więcej postów
  2. Wiele osób może polubić wiele postów. (kompilacja twoich ostatnich 2 wypowiedzi)

ludzie lubią posty

Tabela „Lubię to” będzie miała person_id i post_id jako klucz podstawowy.

Nicolas de Fontenay
źródło