Wdrażanie komentarzy i polubień w bazie danych

146

Jestem programistą. Uwielbiam kodować, ale nie znoszę baz danych ... Obecnie tworzę stronę internetową, na której użytkownik będzie mógł oznaczyć podmiot jako polubiony (jak na FB), otagować go i skomentować .

Utknąłem na projektowaniu tabel bazy danych do obsługi tej funkcji. Rozwiązanie jest banalne, jeśli możemy to zrobić tylko dla jednego rodzaju rzeczy (np. Zdjęć). Ale muszę to włączyć dla 5 różnych rzeczy (na razie, ale zakładam też, że ta liczba może rosnąć w miarę wzrostu całej usługi).

Znalazłem tutaj kilka podobnych pytań, ale żadne z nich nie ma satysfakcjonującej odpowiedzi, więc zadaję to pytanie ponownie.

Pytanie brzmi, jak prawidłowo, wydajnie i elastycznie zaprojektować bazę danych, aby mogła przechowywać komentarze dla różnych tabel , polubień dla różnych tabel i dla nich tagi . Jakiś wzorzec projektowy jako odpowiedź będzie najlepszy;)

Opis szczegółowy : Mam tabelę User z danymi użytkownika i jeszcze 3 tabele : Photoze zdjęciami , Articlesz artykułami , Placesz miejscami . Chcę umożliwić każdemu zalogowanemu użytkownikowi:

  • skomentuj dowolną z tych 3 tabel

  • oznacz dowolne z nich jako lubiane

  • oznacz dowolne z nich jakimś tagiem

  • Chcę też policzyć liczbę polubień dla każdego elementu i ile razy ten konkretny tag został użyty.

1 st podejście :

a) W przypadku tagów będę utworzyć tabelę Tag [TagId, tagName, tagCounter] , a następnie będzie utworzyć wiele do wielu relacje tabel dla: Photo_has_tags, Place_has_tag, Article_has_tag.

b) To samo dotyczy komentarzy.

c) czy utworzyć tablicę LikedPhotos [idUser, idPhoto] , LikedArticles[idUser, idArticle], LikedPlace [idUser, idPlace]. Liczba polubień będzie obliczana na podstawie zapytań (co, jak zakładam, jest złe). I...

Naprawdę nie podoba mi się ten projekt z ostatniej części, źle pachnie;)


2 II podejście :

Utworzę tabelę, w ElementType [idType, TypeName == some table name]której administrator (ja) zapełni nazwy tabel, które można polubić , skomentować lub otagować . Następnie utworzę tabele :

a) LikedElement [idLike, idUser, idElementType, idLikedElement]i to samo dla komentarzy i tagów z odpowiednimi kolumnami dla każdego. Teraz, gdy chcę zrobić zdjęcie polubione, wstawię:

typeId = SELECT id FROM ElementType WHERE TypeName == 'Photo'
INSERT (user id, typeId, photoId)

a dla miejsc:

typeId = SELECT id FROM ElementType WHERE TypeName == 'Place'
INSERT (user id, typeId, placeId)

i tak dalej ... Myślę, że drugie podejście jest lepsze, ale też czuję, że w tym projekcie też czegoś brakuje ...

Wreszcie zastanawiam się, gdzie najlepiej przechowywać licznik, ile razy ten element się podobał. Przychodzą mi do głowy tylko dwa sposoby:

  1. w Photo/Article/Placetabeli element ( )
  2. przez select count ().

Mam nadzieję, że moje wyjaśnienie tej kwestii jest teraz dokładniejsze.

Kokos
źródło
Czy rozważałeś XML?
CodyBugstein,
1
Rzadko znajduję takie pytania, które w 100% są tym, co mam na myśli, Twoje pytanie jest niesamowicie kompletne! Dzięki @Kokos.
aderchox

Odpowiedzi:

195

Najbardziej rozszerzalnym rozwiązaniem jest posiadanie tylko jednej tabeli „bazowej” (połączonej z polubieniami, tagami i komentarzami) i „dziedziczenie” z niej wszystkich innych tabel. Dodanie nowego rodzaju encji polega po prostu na dodaniu nowej „odziedziczonej” tabeli - następnie automatycznie podłącza się ona do całej maszyny like / tag / comment.

Termin „związek encji” to „kategoria” (patrz Przewodnik po metodach ERwin , sekcja: „Relacje podtypów ”). Symbol kategorii to:

Kategoria

Zakładając, że użytkownik może polubić wiele encji, ten sam tag może być użyty dla więcej niż jednej encji, ale komentarz jest specyficzny dla encji, twój model mógłby wyglądać następująco:

Schemat ER


A tak przy okazji, istnieją trzy sposoby na zaimplementowanie „kategorii ER”:

  • Wszystkie typy w jednej tabeli.
  • Wszystkie rodzaje betonu w osobnych tabelach.
  • Wszystkie typy konkretne i abstrakcyjne w oddzielnych tabelach.

O ile nie masz bardzo rygorystycznych wymagań dotyczących wydajności, trzecie podejście jest prawdopodobnie najlepsze (co oznacza, że ​​fizyczne tabele dopasowują 1: 1 jednostki na powyższym diagramie).

Branko Dimitrijevic
źródło
2
świetna odpowiedź, dziękuję. Mam nadzieję, że uda mi się to zaimplementować ... i zastanawiam się, jak Django ORM poradzi sobie z mapowaniem (lub jak zrobię to sam ... ale to drugi problem;)) Ale czy możesz wyjaśnić ja, bo myślę, że nie rozumiem tego właściwie - to, co narysowałeś dla mnie (dzięki!), to trzecie podejście, o którym wspomniałeś?
Kokos
2
@Kokos Zasadniczo podejście (3) oznacza, że ​​ENTITY to stół, PHOTO to stół, ARTICLE to stół, a MIEJSCE to stół. Podejście (2) oznaczałoby, że nie ma tabeli dla ENTITY, a podejście (1) oznaczałoby, że jest tylko jedna tabela. Istnienie wszystkich tych podejść (wszystkie z ich mocnymi i słabymi stronami) jest niefortunną konsekwencją faktu, że typowy RDBMS nie obsługuje natywnie dziedziczenia tabel.
Branko Dimitrijevic
1
+1 dzięki za świetne wyjaśnienie i odniesienia do „kategorii”. Zamierzałem zadać pytanie bliskie temu, ale odpowiedziałeś na nie tutaj.
andy holaday,
2
@BrankoDimitrijevic Dlaczego tabele encji Photo, Article, Place nie mogą mieć własnego PK, np. PhotoID, ArticleID itp., Ale mają również inną kolumnę dla Entity_ID jako FK? Czy to jest niepotrzebne?
tom pierwszy
3
@Orion Maksymalna wartość dla BIGINTto 9223372036854775807. Zakładając, że wstawiasz jeden wiersz na sekundę, za ok. 300 miliardów lat skończą się dostępne wartości. Z pewnością do tego czasu będziesz w stanie przenosić na 128-bitowe liczby całkowite!
Branko Dimitrijevic
22

Skoro „nienawidzisz” baz danych, dlaczego próbujesz je wdrożyć? Zamiast tego poproś o pomoc kogoś, kto kocha te rzeczy i je oddycha.

W przeciwnym razie naucz się kochać swoją bazę danych. Dobrze zaprojektowana baza danych upraszcza programowanie, projektowanie serwisu i usprawnia jego dalsze działanie. Nawet doświadczony projektant d / b nie będzie miał pełnego i doskonałego przewidywania: niektóre zmiany schematu będą potrzebne, gdy pojawią się wzorce użytkowania lub zmienią się wymagania.

Jeśli jest to projekt jednoosobowy, zaprogramuj interfejs bazy danych na proste operacje przy użyciu procedur składowanych: add_user, update_user, add_comment, add_like, upload_photo, list_comments, itd. Nie osadzaj schematu nawet w jednej linii kodu. W ten sposób schemat bazy danych można zmienić bez wpływu na kod: tylko procedury składowane powinny wiedzieć o schemacie.

Może być konieczne kilkakrotne refaktoryzowanie schematu. To normalne. Nie martw się, że za pierwszym razem zrobisz to idealnie. Po prostu spraw, by był wystarczająco funkcjonalny, aby prototypować początkowy projekt. Jeśli masz luksus czasu, użyj go trochę, a następnie usuń schemat i zrób to ponownie. Za drugim razem zawsze jest lepiej.

wallyk
źródło
2
Ponieważ muszę to zaimplementować samodzielnie. Przynajmniej na razie ... i pomyślałem, że może to dobra okazja, aby trochę polubić bazy danych;) Dziękuję za sugestię dotyczącą procedury składowanej. Czy ktoś wie, czy są automatycznie mapowane przez Django ORM?
Kokos
6
Uwielbiam twoje ostatnie zdanie - za drugim razem zawsze jest lepiej.
Lewis
2
Za drugim razem zawsze jest lepiej. Tak
Gammer
20

To jest ogólna idea. Nie zwracaj większej uwagi na stylizację nazw pól, a bardziej na relacje i strukturę

wprowadź opis obrazu tutaj

Ten pseudokod będzie zawierał wszystkie komentarze do zdjęcia o identyfikatorze 5
SELECT * FROM actions
WHERE actions.id_Stuff = 5
AND actions.typeStuff = "photo"
AND actions.typeAction = "comment"

Ten pseudokod otrzyma wszystkie polubienia lub użytkowników, którzy polubili zdjęcie o identyfikatorze 5
(możesz użyć count (), aby po prostu uzyskać liczbę polubień)

SELECT * FROM actions  
WHERE actions.id_Stuff = 5  
AND actions.typeStuff="photo"  
AND actions.typeAction = "like"  
user964260
źródło
Myślę, że możesz nawet polubić komentarze, na przykład kliknięcie linku „Lubię to” w komentarzu. To zapytanie otrzyma polubienia komentarza (akcji) o identyfikatorze 133: SELECT * FROM actions WHERE actions.id=133 AND actions.typeStuff = "comment" AND actions.typeAction = "like"
user964260
1
Na pewno zapamiętam to rozwiązanie dla kolejnych wydań mojego systemu :)
Kokos
Mam 2 tabele rzeczy stuff1 i stuff2 ... Postępowałem zgodnie z tym schematem, ale podczas używania tego ... stuff1, stuff2 są dwie niezależne tabele z niezależnymi kluczami podstawowymi, a tabela akcji ma kolumnę id_stuff, do której odwołuje się te dwie tabele rzeczy1, rzeczy2. Teraz na przykład stuff1 ma 5 wierszy, stuff2 ma 10 wierszy, kiedy próbuję dodać wiersz w tabeli akcji z id_stuff mniej niż 5, powiedzmy „3”, wykonuje zapytanie, ponieważ istnieje wiersz z id_stuff „3” w obu stuff1 i stuff2, ale jeśli spróbuję dodać wiersz z id_stuff większym niż 5 ... (przejdź do następnego komentarza)
vikas devde Kwietnia
1
Wdrażanie polubień w ten sposób utrudnia powiadamianie użytkownika o nowych polubieniach. Wymagałoby to innego stołu.
Greg L
4
W jaki sposób id_stuffkolumna będzie zawierała unikalne wartości w każdej z trzech tabel?
tom pierwszy
0

o ile rozumiem. wymaganych jest kilka tabel. Jest między nimi wiele do wielu relacji.

  • Tabela przechowująca dane użytkownika takie jak imię, nazwisko, data urodzenia wraz z polem tożsamości.
  • Tabela przechowująca typy danych. mogą to być zdjęcia, udostępnienia, linki. każdy typ musi mieć unikalną tabelę. dlatego istnieje związek między ich indywidualnymi tabelami a tą tabelą.
  • każdy typ danych ma swoją tabelę. na przykład aktualizacje statusu, zdjęcia, linki.
  • ostatnia tabela jest dla wielu do wielu relacji przechowujących identyfikator, identyfikator użytkownika, typ danych i identyfikator danych.
erencan
źródło
jeśli opublikujesz diagram bazy danych. mogę narysować relację.
erencan
0

Spójrz na wzorce dostępu, których będziesz potrzebować. Czy któryś z nich wydaje się szczególnie utrudniać lub nieefektywny mój jeden lub drugi wybór dotyczący projektu?

Jeśli nie, faworyzuj tego, który wymaga mniejszej liczby stołów

W tym przypadku:

  1. Dodaj komentarz: albo wybierasz konkretną tabelę wiele / wiele, albo wstawiasz do wspólnej tabeli ze znanym konkretnym identyfikatorem tego, co jest lubiane, myślę, że kod klienta będzie nieco prostszy w drugim przypadku.
  2. Znajdź komentarze do elementu: tutaj wydaje się, że użycie wspólnej tabeli jest nieco łatwiejsze - mamy tylko jedno zapytanie sparametryzowane przez typ jednostki
  3. Znajdź komentarze osoby na temat jednego rodzaju rzeczy: w obu przypadkach proste zapytanie
  4. Znajdź wszystkie komentarze osoby na temat wszystkich rzeczy: w każdym razie wydaje się to trochę dziwne.

Myślę, że twoje „zróżnicowane” podejście, opcja 2, daje w niektórych przypadkach prostsze zapytania i nie wydaje się dużo gorsze w innych, więc wybiorę to.

djna
źródło
0

Zdecydowanie wybierz drugie podejście, w którym masz jedną tabelę i przechowujesz typ elementu dla każdego wiersza, da ci to znacznie większą elastyczność. Zasadniczo, gdy logicznie można coś zrobić z mniejszą liczbą tabel, prawie zawsze lepiej jest mieć mniej tabel. Jedna zaleta, która przychodzi mi do głowy w tej chwili w twoim konkretnym przypadku, rozważ, że chcesz usunąć wszystkie polubione elementy określonego użytkownika, przy pierwszym podejściu musisz wysłać jedno zapytanie dla każdego typu elementu, ale przy drugim podejściu można to zrobić z tylko jednym zapytaniem lub rozważ, gdy chcesz dodać nowy typ elementu, przy pierwszym podejściu polega to na utworzeniu nowej tabeli dla każdego nowego typu, ale przy drugim podejściu nie powinieneś nic robić ...

nikt
źródło
-1

Rozważ użycie tabeli na jednostkę dla komentarzy itp. Więcej tabel - lepsze dzielenie na fragmenty i skalowanie. Nie jest problemem kontrolowanie wielu podobnych tabel dla wszystkich znanych mi frameworków.

Pewnego dnia będziesz musiał zoptymalizować odczyty z takiej struktury. Możesz łatwo tworzyć tabele agragacyjne nad podstawowymi i trochę stracić na zapisach.

Jeden duży stół ze słownikiem może pewnego dnia stać się niekontrolowany.

Oroboros102
źródło
Więcej tabel oznacza, że ​​będzie trudniej go utrzymać. Poszczególne tabele mogą być podzielone na fragmenty przez większość d / bs.
wallyk