Z mojego doświadczenia wynika, że wiele projektów, które przeczytałem w przeszłości, nie zawierało definicji relacji w bazie danych, a jedynie zdefiniowało je w kodzie źródłowym. Zastanawiam się więc, jakie są zalety / wady definiowania relacji między tabelami w bazie danych i kodzie źródłowym? A szersze pytanie dotyczy innych zaawansowanych funkcji współczesnych baz danych, takich jak kaskada, wyzwalacze, procedury ... W moich myślach są pewne kwestie:
W bazie danych:
Prawidłowe dane z projektu. Zapobiegaj błędom aplikacji, które mogą powodować nieprawidłowe dane.
Ogranicz liczbę podróży w obie strony do aplikacji podczas wstawiania / aktualizowania danych, ponieważ aplikacja musi wykonać więcej zapytań, aby sprawdzić integralność danych.
W kodzie źródłowym:
Bardziej elastyczne.
Lepsze przy skalowaniu do wielu baz danych, ponieważ czasami relacja może być między bazami danych.
Większa kontrola nad integralnością danych. Baza danych nie musi sprawdzać za każdym razem, gdy aplikacja modyfikuje dane (złożoność może wynosić O (n) lub O (n log n) (?)). Zamiast tego jest delegowany do aplikacji. I myślę, że obsługa integralności danych w aplikacji spowoduje więcej pełnych komunikatów o błędach niż korzystanie z bazy danych. Np .: kiedy tworzysz serwer API, jeśli zdefiniujesz relacje w bazie danych i coś pójdzie nie tak (np. Encja, do której istnieje odwołanie, nie istnieje), otrzymasz wyjątek SQL z komunikatem. Prostym sposobem będzie zwrócenie klientowi 500, że występuje „Wewnętrzny błąd serwera” i klient nie będzie miał pojęcia, co się dzieje. Lub serwer może przeanalizować komunikat, aby dowiedzieć się, co jest nie tak, co moim zdaniem jest brzydkie i podatne na błędy. Jeśli pozwolisz aplikacji to obsłużyć,
Czy jest coś jeszcze?
Edycja: jak wskazuje Kilian, moje zdanie na temat wydajności i integralności danych jest bardzo mylne. Zedytowałem więc, żeby poprawić mój punkt. Całkowicie rozumiem, że pozwolenie na obsługę bazy danych będzie bardziej wydajnym i niezawodnym podejściem. Sprawdź zaktualizowane pytanie i podziel się z nim przemyśleniami.
Edycja: dziękuję wszystkim. Odpowiedzi, które otrzymałem, wskazują, że ograniczenia / relacje powinny być zdefiniowane w bazie danych. :) Mam jeszcze jedno pytanie, ponieważ jest ono całkowicie poza zakresem tego pytania, właśnie opublikowałem je jako osobne pytanie: Obsługa błędu bazy danych dla serwera API . Proszę zostawić pewne spostrzeżenia.
Odpowiedzi:
TL; DR: Ograniczenia relacji powinny iść do bazy danych.
Twoja aplikacja nie jest wystarczająco duża.
Masz rację, że wymuszanie relacji między bazami danych może wymagać egzekwowania ich w aplikacji.
Chciałbym jednak zwrócić uwagę, że najpierw powinieneś sprawdzić dokumentację używanego oprogramowania bazy danych i sprawdzić istniejące oferty produktów. Na przykład, są Postgres i MySQL.
I nawet jeśli skończy się na tym, że musisz mieć jakieś potwierdzenie w aplikacji, nie wylewaj dziecka z kąpielą . W końcu im mniej musisz zrobić, tym lepiej.
Wreszcie, jeśli martwisz się o przyszłe problemy ze skalowalnością, obawiam się, że twoja aplikacja będzie musiała przejść znaczące zmiany, zanim będzie mogła się skalować. Zasadniczo za każdym razem, gdy powiększysz 10-krotnie, musisz przeprojektować ... więc nie zatapiajmy zbyt dużych pieniędzy w nieumiejętność przewidywania problemów ze skalowalnością, a zamiast tego używaj pieniędzy, aby faktycznie dotrzeć do punktu, w którym masz te problemy.
Twoja aplikacja nie jest wystarczająco poprawna.
Jaka jest szansa, że używana baza danych ma wadliwą implementację czeku, w porównaniu z szansą, że twoja aplikacja ma wadliwą implementację czeku?
A który z nich zmieniasz najczęściej?
Założę się, że baza danych jest poprawna w dowolnym momencie .
Twoi programiści nie myślą wystarczająco dobrze.
Czerwona flaga ! 1
Jeśli myślisz:
wtedy nie powiodła się najbardziej podstawowa kwestia współbieżności: inny proces / wątek mógł dodawać rekord podczas pracy.
Jeśli myślisz:
wtedy nie udało Ci się rozliczyć MVCC: widok posiadanej bazy danych jest migawką w momencie rozpoczęcia transakcji; to jednak nie pokazać wszystkie aktualizacje, które mają miejsce, a może nawet nie popełnione.
Utrzymywanie ograniczeń w wielu sesjach jest naprawdę trudnym problemem, ciesz się, że zostało rozwiązane w bazie danych.
1 Chyba że baza danych poprawnie implementuje właściwość Serializable; ale niewielu tak.
Ostatni, ubiegły, zeszły:
Nie analizuj komunikatów o błędach , jeśli używasz bazy danych klasy produkcyjnej, powinna zwracać błędy strukturalne. Będziesz mieć przynajmniej kod błędu, aby wskazać, co może być nie tak, i na podstawie tego kodu możesz stworzyć odpowiedni komunikat o błędzie.
Zauważ, że w większości przypadków wystarczy kod: jeśli masz kod błędu informujący, że klucz obcy, do którego istnieje odwołanie, nie istnieje, oznacza to, że ta tabela ma tylko jeden klucz obcy, więc wiesz w kodzie, na czym polega problem .
I, bądźmy szczerzy tutaj, przez większość czasu i tak nie będziesz w stanie z wdziękiem obsługiwać błędów. Tylko dlatego, że jest ich tak wiele, że nie zdołasz uwzględnić ich wszystkich ...
... który tylko wiąże się z punktem poprawności powyżej. Za każdym razem, gdy pojawia się komunikat „500: Wewnętrzny błąd serwera”, ponieważ uruchomiono ograniczenie bazy danych i nie zostało ono obsłużone, oznacza to, że baza danych Cię uratowała, ponieważ zapomniałeś obsługiwać ją w kodzie.
źródło
To głęboko błędny punkt. Właśnie w tym celu stworzono bazy danych. Jeśli potrzebujesz sprawdzania integralności danych (i jeśli uważasz, że ich nie potrzebujesz, prawdopodobnie się mylisz), to pozwolenie na obsługę danych przez bazę danych jest prawie na pewno bardziej wydajne i mniej podatne na błędy niż robienie tego w logice aplikacji.
źródło
Ograniczenia powinny leżeć w bazie danych, ponieważ (przy najlepszej woli na świecie) twoja aplikacja nie będzie jedyną rzeczą, aby kiedykolwiek uzyskać dostęp do tej bazy danych.
W pewnym momencie może zaistnieć potrzeba skryptu w bazie danych lub migracja danych z jednej tabeli do drugiej podczas wdrażania.
Dodatkowo możesz spełnić inne wymagania, np. „Duży klient X naprawdę potrzebuje tego arkusza Excela importowanego do naszej bazy danych aplikacji po południu”, w którym nie będziesz miał luksusu, dostosowując kod aplikacji, aby pasował do sytuacji, gdy zrobi to brudny skrypt SQL w samą porę.
To tutaj integralność na poziomie bazy danych uratuje twój bekon.
Dodatkowo, wyobraź sobie programistę, który po odejściu przejmuje Twoją rolę w tej firmie, a następnie ma za zadanie wprowadzić zmiany w bazie danych.
Czy będzie cię nienawidził, jeśli w bazie danych nie ma żadnych ograniczeń FK, aby mógł powiedzieć, jakie relacje ma tabela przed jej zmianą? ( Wskazówka, odpowiedź brzmi tak )
źródło
Powinieneś mieć relacje w bazie danych.
Jak zauważa inna odpowiedź, wydajność sprawdzania ograniczeń będzie znacznie lepsza w tej bazie danych niż w aplikacji. Sprawdzanie ograniczeń bazy danych jest jedną z rzeczy, w których bazy danych są dobre.
Jeśli kiedykolwiek potrzebujesz dodatkowej elastyczności - np. Zanotowanych odniesień do bazy danych - możesz usunąć ograniczenia umyślnie i rozważnie. Spójność w bazie danych oznacza, że masz możliwość modyfikacji tych ograniczeń i pewność integralności referencyjnej.
źródło
Czy naprawdę stać Cię na pisanie i testowanie kodu egzekwowania integralności referencyjnej, gdy masz kod do rozwiązywania problemów z domeną?
źródło
Jeśli nie sprawdzisz integralności danych, ograniczeń, relacji itp. Na poziomie bazy danych, oznacza to, że każdy, kto ma dostęp do produkcyjnej bazy danych (za pośrednictwem dowolnego innego klienta, w tym narzędzia dostępu do bazy danych), może znacznie łatwiej popsuć dane.
Dobrą praktyką jest egzekwowanie jak najściślejszej integralności danych na poziomie bazy danych. Zaufaj mi, zaoszczędzi to z czasem ogromnych bólów głowy w każdym nietrywialnym systemie. Będziesz także szybciej wychwytywać błędy logiczne aplikacji lub błędy wymagań biznesowych i niespójności, jeśli się nad tym zastanowisz.
Na marginesie należy zaprojektować bazę danych w sposób możliwie najbardziej znormalizowany i atomowy. Brak tabel „Boga”. Poświęć dużo wysiłku, aby twoja baza danych była jak najprostsza, najlepiej z wieloma małymi tabelami, które są indywidualnie bardzo dobrze zdefiniowane, z jedną odpowiedzialnością i dokładnie sprawdzone we wszystkich kolumnach. Baza danych jest ostatnim strażnikiem integralności danych. Reprezentuje Twierdzę Zamku.
źródło
Większość ludzi zasadniczo mówi „tak, generalnie zawsze będziesz definiować relacje w bazie danych”. Ale gdyby dyscypliny informatyczne były tak łatwe, nazwalibyśmy się „Software Manual Readers” zamiast „Software Engineers”. W rzeczywistości zgadzam się, że ograniczenia powinny iść do bazy danych, chyba że istnieje dobry powód, dla którego nie powinny , dlatego przedstawię kilka powodów, które można uznać za dobre w pewnych sytuacjach:
Duplikat kodu
Czasami w kodzie aplikacji będzie naturalnie istniała pewna ilość funkcji, którą mogłaby obsłużyć baza danych. Jeśli dodanie czegoś takiego jak ograniczenia do bazy danych byłoby zbędne, może być lepiej nie powielać funkcjonalności, ponieważ naruszasz zasady DRY, a możesz pogorszyć żonglujący akt utrzymywania synchronizacji bazy danych i kodu aplikacji.
Wysiłek
Jeśli Twoja baza danych już działa tak, jak powinna, bez korzystania z zaawansowanych funkcji, warto ocenić, gdzie należy poświęcić czas, pieniądze i wysiłek. Jeśli dodanie ograniczeń zapobiegnie katastrofalnej awarii, a tym samym zaoszczędzi Twojej firmie dużo pieniędzy, to prawdopodobnie warto. Jeśli dodajesz ograniczenia, które powinny obowiązywać, ale gwarantują, że nigdy nie zostaną naruszone, tracisz czas i zanieczyszczasz bazę kodu. Gwarantowane jest tutaj słowo operacyjne.
Wydajność
Zwykle nie jest to dobry powód, ale w niektórych przypadkach możesz mieć pewne wymagania dotyczące wydajności. Jeśli kod aplikacji może zaimplementować określoną funkcjonalność w szybszy sposób niż baza danych, a potrzebujesz dodatkowej wydajności, może być konieczne zaimplementowanie tej funkcji w kodzie aplikacji.
Kontrola
Nieco związane z wydajnością. Czasami potrzebujesz bardzo drobiazgowej kontroli nad tym, jak funkcja jest implementowana, a czasem obsługa bazy danych ukrywa ją za czarną skrzynką, którą musisz otworzyć.
Punkty końcowe
Ostatnią rzeczą, którą powiem, jest to, że będziesz wiedział, czy nie powinieneś umieszczać funkcji w bazie danych. Jeśli nie masz pewności, prawdopodobnie lepiej będzie skorzystać z funkcji bazy danych, ponieważ zwykle działają one naprawdę dobrze.
źródło
Jak zawsze jest wiele odpowiedzi. Dla mnie znalazłem prostą zasadę (dobrze działa tylko w przypadku podejścia zorientowanego na model). Zwykle koncentruję się tylko na różnych warstwach aplikacji.
Jeśli model składa się z kilku podmiotów i istnieją między nimi zależności, warstwa trwałości powinna odzwierciedlać te zależności wraz z ich możliwościami. Więc jeśli używasz RDBMS, powinieneś również użyć kluczy obcych. Powód jest prosty. W ten sposób dane są zawsze poprawne strukturalnie.
Każda instancja wykonująca pracę na tej warstwie trwałości może na niej polegać. Zakładam, że hermetyzujesz tę warstwę za pomocą interfejsu (usługi). Oto punkt, w którym kończy się projektowanie i zaczyna się prawdziwy świat.
Patrząc na swoje punkty, zwłaszcza odniesienia między bazami danych . W takim przypadku tak nie powinno być zaimplementowane odwołanie w samym RDBMS, ale w usłudze. Ale czy przed pójściem w ten sposób nie byłoby lepiej rozważyć to już podczas projektowania?
Znaczy, jeśli już wiem, że istnieją części, które muszą być przechowywane w innym DB, to mogę je już tam umieścić i zdefiniować jako osobny model. Dobrze?
Wskazujesz również, że implementacja tego w kodzie jest bardziej elastyczna . Zgadza się, ale czy to nie brzmi, jakbyś miał do czynienia z niepełnym projektem? Zadaj sobie pytanie, dlaczego potrzebujesz większej elastyczności?
Problem z wydajnością wynikający z kontroli integralności w bazie danych nie jest prawdziwy. RDBMS może sprawdzić takie rzeczy znacznie szybciej niż jakakolwiek implementacja przez ciebie. Dlaczego? Cóż, musisz poradzić sobie z zakłóceniami mediów, RDBMS nie. I może zoptymalizować takie kontrole, używając swoich statystyk aso
Widzisz, wszystko wraca do projektowania. Oczywiście możesz powiedzieć teraz, ale co, jeśli pojawi się nieznany wymóg, zmieniacz gier? Tak, może się zdarzyć, ale takie zmiany powinny zostać zaprojektowane i zaplanowane aso. ; o)
źródło
Masz kilka bardzo dobrych odpowiedzi, ale więcej punktów
Integralność danych jest tym, do czego służy baza danych
Wykonanie odpowiedniej współbieżności, takiej jak usuwanie FK na poziomie aplikacji, byłoby przerażające
Specjalizacja w zakresie integralności danych opiera się na DBA
Na poziomie programu wstawiasz, aktualizujesz, aktualizujesz zbiorczo, wstawiasz zbiorczo, kasujesz zbiorczo ...
Cienki klient, gruby klient, klient mobilny ....
Integralność danych nie jest wiedzą programisty - dużo duplikatu kodu i ktoś będzie bałagan to w górę
Powiedzmy, że zostałeś zhakowany - i tak masz kłopoty, ale haker może wyrządzić wiele szkód przez małą dziurę, jeśli w bazie danych nie ma ochrony integralności
Może być konieczne manipulowanie danymi bezpośrednio za pomocą SQL lub TSQL.
Nikt nie zapamięta wszystkich reguł dotyczących danych
źródło
Twoje pytanie nie ma sensu: jeśli możesz zmienić bazę danych, to jest kod, jeśli nie możesz zmienić bazy danych, będziesz musiał stworzyć ograniczenia gdzie indziej.
Baza danych, którą możesz zmienić, to tyle samo kodu, co dowolna linia ruby, javascript, c # lub ada.
Pytanie o miejsce ograniczenia w systemie powinno sprowadzać się do niezawodności, kosztów i łatwości rozwoju.
źródło
Tutaj jest mnóstwo dobrych odpowiedzi. Dodam, że jeśli masz aplikację napisaną w języku Y, możesz utworzyć kod podobny do bazy danych w Y. A następnie ktoś chce uzyskać dostęp do Twojej bazy danych w języku Z, musisz ponownie napisać ten sam kod. Niech Bóg ci pomoże, jeśli implementacje nie są dokładnie takie same. Lub gdy doświadczony użytkownik biznesowy połączy się z bazą danych za pomocą Microsoft Access.
Z mojego doświadczenia wynika, że kiedy ludzie nie chcą używać ograniczeń bazy danych, to dlatego, że faktycznie próbują zrobić coś w niewłaściwy sposób. Na przykład próbują masowo ładować dane i na jakiś czas chcą pozostawić kolumny inne niż null. Zamierzają „naprawić to później”, ponieważ sytuacja, która spowodowała, że ograniczenie niepuste ma krytyczne znaczenie „nie może się zdarzyć w tym przypadku”. Innym przykładem może być próba połączenia dwóch różnych typów danych w tej samej tabeli.
Bardziej doświadczeni ludzie cofną się o krok i znajdą rozwiązanie, które nie wymaga próby ominięcia ograniczenia. Rozwiązaniem może być po prostu ograniczenie, które nie jest już właściwe, ponieważ firma oczywiście się zmieniła.
źródło