Dlaczego model relacyjny dla bazy danych ma znaczenie?

61

Zbliżam się do projektu, w którym będę musiał wdrożyć bazę danych z moim szefem; jesteśmy bardzo małym przedsiębiorstwem rozpoczynającym działalność, więc środowisko pracy jest głęboko osobiste.

Dał mi wcześniej jedną z firmowych baz danych i było to całkowicie sprzeczne z tym, czego nauczono mnie (i czytałem) w szkole dla RDBMS. Na przykład istnieją tutaj całe bazy danych, które składają się z jednej tabeli (na niezależną bazę danych). Jedna z tych tabel ma ponad 20 kolumn i dla kontekstu oto niektóre nazwy kolumn z jednej tabeli:

lngStoreID | vrStoreName | lngCompanyID | vrCompanyName | lngProductID | vrProductName

Chodzi o to, że tam, gdzie powinien mieć indywidualne tabele, które przechowują dane jednostki (nazwa, rozmiar, data zakupu itp.), Umieszcza to wszystko w jednej dużej tabeli na bazę danych.

Chcę ulepszyć ten projekt, ale nie jestem pewien, dlaczego właściwie znormalizowany i segmentowany model danych rzeczywiście ulepszyłby ten produkt. Chociaż jestem zaznajomiony z projektowaniem baz danych na studiach i rozumiem, jak to zrobić, nie jestem pewien, dlaczego to faktycznie poprawia bazy danych.

Dlaczego dobry schemat relacyjny poprawia bazę danych?

8 protonów
źródło
33
Jedno słowo: normalizacja.
Robert Harvey
9
Zamknij wyborcę - uzasadnij się! :-)
Robbie Dee,
6
Nowi pracownicy często krytykują ustalone procedury bez zrozumienia przyczyn leżących u ich podstaw, nawet jeśli przyczyny te nie są uzasadnione technicznie. Najpierw dowiedz się, dlaczego twój szef tak to zbudował. Może on / ona bardzo dobrze wiedzieć, że nie jest to dobry projekt, ale nie ma wiedzy (a raczej czasu), aby zrobić to lepiej. Wszelkie zaproponowane przez Ciebie zmiany zostaną prawdopodobnie pozytywnie odebrane, jeśli z szacunkiem potwierdzisz powody obecnego projektu.
Pedro
5
He [the boss] had given me one of his databases before and it completely went against what I was taught (and read about) in school for RDBMS<- Witamy w prawdziwym świecie!
Möoz 27.04.16
5
Przypomina mi się mój ulubiony relacyjny cytat z bazy danych: „Normalizuj, aż boli, denormalizuj, aż działa”
Jake,

Odpowiedzi:

70

Argument dotyczący wydajności jest zwykle najbardziej intuicyjny. W szczególności chcesz wskazać, jak trudno będzie dodać dobre indeksy w nieprawidłowo znormalizowanej bazie danych (uwaga: istnieją przypadki skrajne, w których denormalizacja może w rzeczywistości poprawić wydajność, ale gdy obaj nie mają doświadczenia w relacyjnych bazach danych, prawdopodobnie nie będzie łatwo patrz te przypadki).

Kolejnym argumentem jest rozmiar pamięci. Zdenormalizowany stół z dużą ilością zwolnień będzie wymagał znacznie więcej miejsca. Wpływa to również na aspekt wydajności: im więcej danych masz, tym wolniejsze będą twoje zapytania.

Jest też argument, który jest nieco trudniejszy do zrozumienia, ale w rzeczywistości jest ważniejszy, ponieważ nie można go rozwiązać, rzucając na niego więcej sprzętu. To jest problem spójności danych. Właściwie znormalizowana baza danych sama zadba o to, aby produkt o określonym identyfikatorze zawsze miał tę samą nazwę. Ale w zdecentralizowanej bazie danych takie niespójności są możliwe, dlatego należy zachować szczególną ostrożność, jeśli chodzi o unikanie niespójności, co zajmie czas programowania w celu prawidłowego wykonania i nadal będzie powodować błędy, które będą cię kosztować satysfakcję klienta.

Philipp
źródło
19
Jedną z głównych zalet denormalizacji jest magazynowanie danych , szczególnie jeśli masz dużą ilość danych, która gwarantuje, że nigdy się nie zmieni, i chcesz szybciej i wydajniej wyszukiwać je kosztem przestrzeni dyskowej. Dobra odpowiedź, to tylko informacja dla każdego początkującego SQL, który nie jest pewny, dlaczego coś innego niż 3NF byłoby pożądane.
11
Nie jestem pewien, dlaczego argument dotyczący spójności jest „trudniejszy do zrozumienia”. Wydaje mi się to o wiele prostsze: jeśli wartość się zmienia, wówczas wszystkie kopie tej wartości muszą zostać zaktualizowane. Aktualizacja pojedynczej kopii jest znacznie mniej podatna na błędy niż aktualizacja setek lub tysięcy kopii tych samych danych. Dotyczy to równie dobrze relacji między danymi. (Jeśli relację przechowuję na dwa sposoby, muszę zaktualizować obie kopie relacji.) Jest to niezwykle powszechny problem w zdenormalizowanych bazach danych; to bardzo trudno zapobiec korupcji w praktyce (wyjątek zmaterializował widok typu użytkowania).
jpmc26
4
Ostatni akapit należy wyróżnić pogrubioną czcionką. :-) Bez normalizacji nie można zagwarantować integralności danych. Kontrola danych wejściowych wyłącznie w warstwie Business Logic jest głupcem, ponieważ każda nienormalizowana baza danych ostatecznie wykazuje pewną anomalię danych.
DanK
2
@ IsmaelMiguel Zwyczajową praktyką jest to, że takie dane podstawowe nigdy nie są trwale usuwane z bazy danych. Usuwasz go tylko miękko, ustawiając na nim flagę informującą, że nie jest już dostępny. W tym konkretnym przypadku dobrym pomysłem byłoby posiadanie relacji klucza obcego między produktami a zamówieniami, co oznacza, że ​​baza danych zgłasza błąd przy próbie usunięcia produktu, do którego odnoszą się dowolne zamówienia.
Philipp
24

Będę musiał zaimplementować bazę danych z moim szefem ...

Korzystanie z dedykowanego oprogramowania do zarządzania bazami danych może być znacznie łatwiejsze (przepraszam; nie mogłem się oprzeć).

lngStoreID | vrStoreName | lngCompanyID | vrCompanyName | lngProductID | vrProductName

Jeśli ta baza danych dba tylko o „rejestrowanie”, który produkt został sprzedany, gdzie, kiedy i przez kogo, być może będziesz w stanie rozciągnąć Definicję „OK bazy danych” wystarczająco daleko, aby ją objąć. Jeśli te dane są wykorzystywane do czegokolwiek innego, to jest naprawdę kiepskie.

Ale ...

Czy aplikacja / zapytania wykorzystujące te dane reagują słabo / powoli? Jeśli nie, to nie ma prawdziwego problemu do rozwiązania. Jasne, wygląda i czuje się brzydko, ale jeśli to działa , nie dostaniesz żadnych „punktów” za sugerowanie, że „mogłoby” być lepsze.

Jeśli możesz znaleźć wyraźne objawy (tj. Problemy), które wyglądają, jakby były spowodowane złym modelowaniem danych, prototypuj lepsze rozwiązanie. Weź kopię jednej z tych „baz danych”, znormalizuj dane i sprawdź, czy Twoje rozwiązanie działa lepiej. Jeśli jest znacznie lepszy (i w pełni oczekiwałbym, że wszelkie operacje aktualizacji na tych danych zostałyby znacznie poprawione), wróć do swojego szefa i pokaż mu poprawę.

Zupełnie możliwe jest odtworzenie jego „widoku pojedynczej tabeli” danych za pomocą… cóż… widoków.

Phill W.
źródło
11
Odporność na pojedynczą tabelę weltanschauung często pochodzi od osób niedoświadczonych w SQL, które nie rozumieją złączeń - szczególnie w odniesieniu do brakujących danych, tj. Złączeń zewnętrznych.
Robbie Dee,
6
@RobbieDee Częściej pochodzi od osób, które widziały, jak zdormalizowane dane ulegają uszkodzeniu przez niespójność. Jestem jedną z takich osób. Rozważałbym tylko tego rodzaju strukturę w sytuacji, którą sugeruje Phill: jest to pewnego rodzaju tabela rejestrowania / raportowania, w której dane nigdy nie będą aktualizowane lub tylko aktualizowane poprzez usunięcie danych i całkowite odzyskanie z innych źródeł.
jpmc26
2
Nawet jeśli aplikacja działa zadowalająco z taką bazą danych, nadal nie jest elastyczna jak poprawnie znormalizowana baza danych. Jeśli nazwa sklepu lub nazwa firmy ulegną zmianie, należy ją zaktualizować wszędzie, a nie tylko w tabeli sklepu lub firmy. W niektórych przypadkach może to być dokładnie to, czego chcesz (na przykład, jeśli dane są gromadzone głównie w celach archiwalnych), ale musimy wiedzieć więcej o konkretnej aplikacji.
Zach Lipton
1
@Zach: uzgodniono, dlatego dziennik sprzedaży jest potencjalnie do przyjęcia. Załóżmy, że chcesz, aby każda sprzedaż była powiązana z nazwą sklepu w momencie jej sprzedaży, a nie „bieżącą nazwą sklepu”, a następnie próba „normalizacji” wprowadza to znaczną złożoność (ponieważ nazwy sklepów z tabelami do zapisu z czasem będzie to seria, a nie tylko jedna wartość na storeid)
Steve Jessop
Być może ogólną zasadą byłoby, że jeśli jedyną złożonością wprowadzoną przez proponowaną normalizację jest to, że kilka zapytań wymaga teraz dołączenia w nich, aby wybrać wszystkie kolumny, które muszą zgłosić, to nie powinieneś biegać, aby wprowadzić tę zmianę: )
Steve Jessop
14

Dlaczego dobry schemat relacyjny poprawia bazę danych?

Odpowiedź brzmi: nie zawsze poprawia bazę danych. Powinieneś być świadomy, że to, czego prawdopodobnie nauczyłeś, nazywa się Trzecią Normalną Formą .

Inne formularze są ważne w niektórych sytuacjach, co jest kluczowe dla udzielenia odpowiedzi na pytanie. Twój przykład wygląda jak Pierwsza Normalna Forma , jeśli to pomaga ci poczuć się lepiej z jej obecnym stanem.

Reguły 3NF ustanawiają relacje między danymi, które „ulepszają” bazę danych:

  1. Zapobiegaj przedostawaniu się nieprawidłowych danych do twojego systemu (jeśli relacja wynosi 1 do 1, wymusza błąd pomimo kodu zapisanego na nim). Jeśli dane są spójne w bazie danych, jest mniej prawdopodobne, że spowoduje to niespójności poza bazą danych.

  2. Zapewnia sposób sprawdzania poprawności kodu (np. Relacja wiele do jednego jest sygnałem ograniczającym właściwości / zachowania obiektu). Podczas pisania kodu w celu korzystania z bazy danych programiści czasami zauważają strukturę danych jako wskaźnik tego, jak powinien działać ich kod. Lub mogą dostarczyć użytecznych informacji zwrotnych, jeśli baza danych nie pasuje do ich kodu. (To niestety przypomina myślenie życzeniowe).

  3. Podaj reguły, które mogą znacznie pomóc w zmniejszeniu liczby błędów podczas budowania bazy danych, aby nie budować jej w oparciu o arbitralne wymagania, które mogą pojawić się w dowolnym momencie życia bazy danych. Zamiast tego systematycznie oceniasz informacje, aby osiągnąć określone cele.

  4. Właściwe struktury bazy danych prowadzą do poprawy wydajności poprzez łączenie danych w sposób, który minimalizuje przechowywanie danych, minimalizuje wywołania pamięci w celu pobrania danych, maksymalizuje zasoby w pamięci i / lub minimalizuje sortowanie / manipulowanie danymi dla konkretnego zestawu danych, w porównaniu do zapytania, którym jesteś wykonanie przeciwko temu. Ale „właściwa” struktura zależy od ilości danych, charakteru danych, rodzaju zapytania, zasobów systemowych itp. Normalizując, możesz pogorszyć wydajność (tj. Jeśli załadujesz wszystkie dane jako 1 tabelę - łączenie może spowolnić zapytanie). Przetwarzanie transakcji (OLTP) a inteligencja biznesowa (hurtownia danych) są bardzo różne.

W małej firmie z małymi zestawami danych może się okazać, że nie ma nic złego w obecnej sytuacji. Tyle, że jeśli dorośniesz, późniejszym problemem będzie „naprawienie”, ponieważ w miarę powiększania się stołu systemy, które go używają, prawdopodobnie będą działać wolniej.

Zazwyczaj będziesz chciał podkreślić szybkie transakcje w miarę rozwoju firmy. Jeśli jednak spędzasz teraz czas na tym projekcie zamiast na inne rzeczy, które firma może potrzebować bardziej pilnie, możesz nigdy nie mieć tego problemu, ponieważ Twoja firma nigdy tak naprawdę nie rośnie. To „wyzwanie przed optymalizacją” - gdzie spędzić teraz swój cenny czas.

Powodzenia!

Jim
źródło
4
Nie wspomniano, ale myślę, że ważną kwestią dla programistów jest to, że edycja jednej „rzeczy” wymaga edycji tylko jednego wiersza, zamiast konieczności zapętlania całej bazy danych w celu znalezienia i zastąpienia tej jednej rzeczy.
slebetman
@slebetman Nigdy nie powinieneś mieć pętli po stronie kodu, aby aktualizować wiele wierszy w jednej tabeli, niezależnie od tego, czy jest ona znormalizowana. Użyj WHEREklauzuli. Oczywiście nadal mogą się nie udać, ale jest to mniej prawdopodobne w znormalizowanej sytuacji, ponieważ wystarczy dopasować tylko jeden wiersz za pomocą klucza podstawowego.
jpmc26
@ jpmc26: Przez zapętlenie bazy danych mam na myśli konstruowanie zapytania w celu zaktualizowania wszystkich dotkniętych wierszy. Czasami wystarczy jedno GDZIE. Ale widziałem bezbożne struktury, które wymagają podselekcji w tej samej tabeli, aby uzyskać wszystkie dotknięte wiersze bez wpływu na wiersze, które nie powinny się zmieniać. Widziałem nawet struktury, gdzie jedno zapytanie nie może wykonać pracę (podmiot, który musi zmienić miejsce zamieszkania w różnych kolumn w zależności od wiersza)
slebetman
Wiele doskonałych odpowiedzi na to pytanie, i to nie był wyjątek.
Mike Chamberlain
11

Istnieje wiele powodów, dla których używanie jednego dużego „stołu bożego” jest złe. Spróbuję zilustrować problemy z utworzoną przykładową bazą danych. Załóżmy, że próbujesz modelować wydarzenia sportowe. Powiemy, że chcesz modelować gry i drużyny grające w te gry. Projekt z wieloma tabelami może wyglądać tak (celowo jest to bardzo uproszczone, więc nie daj się złapać w miejscach, w których można zastosować większą normalizację):

Teams
Id | Name | HomeCity

Games
Id | StartsAt | HomeTeamId | AwayTeamId | Location

i baza danych z pojedynczą tabelą wyglądałaby tak

TeamsAndGames
Id | TeamName | TeamHomeCity | GameStartsAt | GameHomeTeamId | GameAwayTeamId | Location

Najpierw spójrzmy na tworzenie indeksów na tych tabelach. Gdybym potrzebował indeksu dla rodzinnego miasta dla drużyny, mógłbym dość łatwo dodać go do Teamsstołu lub TeamsAndGamesstołu. Pamiętaj, że za każdym razem, gdy tworzysz indeks, musi on być gdzieś przechowywany na dysku i aktualizowany w miarę dodawania wierszy do tabeli. W przypadku Teamsstołu jest to dość proste. Wprowadziłem nowy zespół, baza danych aktualizuje indeks. Ale po co TeamsAndGames? To samo odnosi się doTeamsprzykład. Dodam zespół, indeks się aktualizuje. Ale dzieje się tak również, gdy dodam grę! Mimo że to pole będzie puste dla gry, indeks i tak musi zostać zaktualizowany i zapisany na dysku dla tej gry. Dla jednego indeksu nie brzmi to tak źle. Ale kiedy potrzebujesz wielu indeksów dla wielu jednostek wciśniętych w tę tabelę, marnujesz dużo miejsca na przechowywanie indeksów i mnóstwo czasu procesora na ich aktualizowanie pod kątem rzeczy, których nie dotyczą.

Po drugie, spójność danych. W przypadku korzystania z dwóch oddzielnych stołów, mogę używać kluczy obcych ze Gamesstołu do Teamsstołu, aby określić, które drużyny grają w grę. Zakładając, że nie dopuszczam do zerowania kolumn HomeTeamIdi AwayTeamId, baza danych zapewni, że w każdej grze, w której będę grał, będą 2 drużyny i że te drużyny będą istnieć w mojej bazie danych. Ale co ze scenariuszem pojedynczego stołu? Ponieważ w tej tabeli znajduje się wiele elementów, kolumny te powinny być zerowalne (możesz sprawić, że nie będą zerowalne i wrzuć tam śmieci, ale to tylko okropny pomysł). Jeśli te kolumny są zerowalne, baza danych nie może już zagwarantować, że po wstawieniu gry będą miały dwie drużyny.

Ale co jeśli zdecydujesz się po prostu i tak na to? Skonfiguruj klucze obce w taki sposób, aby pola te wskazywały na inny element w tej samej tabeli. Ale teraz baza danych po prostu upewni się, że te jednostki istnieją w tabeli, a nie, że są one poprawnego typu. Możesz bardzo łatwo ustawić GameHomeTeamIdidentyfikator innej gry, a baza danych w ogóle nie będzie narzekać. Jeśli spróbujesz tego w scenariuszu z wieloma tabelami, baza danych będzie pasować.

Możesz spróbować złagodzić te problemy, mówiąc „cóż, upewnimy się, że nigdy nie zrobimy tego w kodzie”. Jeśli masz pewność, że umiesz pisać kod wolny od błędów po raz pierwszy i że możesz wziąć pod uwagę każdą dziwną kombinację rzeczy, które użytkownik może wypróbować, śmiało. Osobiście nie jestem pewien swojej zdolności do robienia tych rzeczy, więc pozwolę, aby baza danych dała mi dodatkową siatkę bezpieczeństwa.

(Jest jeszcze gorzej, jeśli Twój projekt polega na kopiowaniu wszystkich istotnych danych między wierszami zamiast korzystania z kluczy obcych. Wszelkie niespójności w pisowni / innych danych będą trudne do rozwiązania. Jak rozpoznać, czy „Jon” jest błędem w pisowni „John” „lub jeśli było to zamierzone (ponieważ są to dwie osobne osoby)?)

Po trzecie, prawie każda kolumna musi mieć wartość zerową lub być wypełniona albo skopiowanymi, albo śmieciowymi danymi. Gra nie potrzebuje TeamNamelub TeamHomeCity. Tak więc albo każda gra potrzebuje jakiegoś symbolu zastępczego, albo musi być zerowa. A jeśli jest zerowalne, baza danych chętnie wybierze grę bez TeamName. Zajmie to również zespół bez nazwy, nawet jeśli logika biznesowa mówi, że to nigdy nie powinno się zdarzyć.

Istnieje kilka innych powodów, dla których chciałbyś mieć osobne tabele (w tym zachowanie rozsądku programisty). Istnieje nawet kilka powodów, dla których większy stół może być lepszy (denormalizacja czasami poprawia wydajność). Te scenariusze są nieliczne i dalekie od (i zwykle najlepiej sobie z nimi radzić, gdy masz wskaźniki wydajności, które pokazują, że to naprawdę jest problem, a nie brakujący indeks lub coś innego).

Wreszcie opracuj coś, co będzie łatwe w utrzymaniu. To, że „działa”, nie oznacza, że ​​jest OK. Próba utrzymywania boskich tabel (takich jak boskie klasy) to koszmar. Po prostu szykujesz się na ból później.

Becuzz
źródło
1
„Zespoły: Id | Nazwa | HomeCity”. Tylko upewnij się, że Twój schemat danych nie powoduje, że Twoja aplikacja nieprawidłowo twierdzi, że Super Bowl XXXIV został wygrany przez LA Rams. Podczas gdy SB XXXIV powinien pojawić się w zapytaniu dotyczącym wszystkich mistrzostw wygranych przez zespół obecnie znany jako LA Rams. Są lepsze i gorsze „boskie stoły”, a na pewno przedstawiłeś zły. Lepszym byłoby „ID gry | nazwa drużyny gospodarzy | miasto drużyny gospodarzy | nazwa drużyny gości | miasto drużyny gości | mecz rozpoczyna się o | itd. ...”. Jest to pierwsza próba modelowania informacji takich jak „New Orleans Saints @ Chicago Bears 1p Eastern”.
Steve Jessop
6

Cytat dnia: „ Teoria i praktyka powinny być takie same ... w teorii

Tabela znormalizowana

Twoja unikalna tabela Hold-it-all zawiera nadmiarowe dane ma jedną zaletę: sprawia, że ​​raportowanie na jej liniach jest bardzo łatwe do kodowania i szybkie w wykonaniu, ponieważ nie musisz wykonywać żadnych połączeń. Ale to kosztem:

  • Zawiera zbędne kopie relacji (np. IngCompanyIDI vrCompanyName). Aktualizacja danych podstawowych może wymagać aktualizacji znacznie większej liczby wierszy niż w znormalizowanym schemacie.
  • Miesza wszystko. Nie można zapewnić łatwej kontroli dostępu na poziomie bazy danych, np. Upewnienia się, że użytkownik A może aktualizować tylko informacje o firmie, a użytkownik B tylko informacje o produkcie.
  • Nie można zapewnić reguł spójności na poziomie bazy danych (np. Klucz podstawowy, aby wymusić, że istnieje tylko jedna nazwa firmy dla identyfikatora firmy).
  • Nie w pełni korzystasz z optymalizatora DB, który mógłby zidentyfikować optymalne strategie dostępu dla złożonego zapytania, wykorzystując rozmiar znormalizowanych tabel i statystyki kilku indeksów. Może to szybko zrównoważyć ograniczoną korzyść z unikania połączeń.

Tabela znormalizowana

Powyższe wady to zalety znormalizowanego schematu. Oczywiście zapytania mogą być nieco bardziej skomplikowane do napisania.

Krótko mówiąc, znormalizowany schemat wyraża znacznie lepiej strukturę i relacje między danymi. Będę prowokujący i powiem, że to ta sama różnica niż między dyscypliną wymaganą do korzystania z zestawu zamówionych szuflad biurowych a łatwością korzystania z kosza na śmieci.

Christophe
źródło
5

Myślę, że twoje pytanie obejmuje co najmniej dwie części:

1. Dlaczego jednostki różnych typów nie powinny być przechowywane w tej samej tabeli?

Najważniejsze odpowiedzi tutaj to czytelność kodu i szybkość. A SELECT name FROM companies WHERE id = ?jest o wiele bardziej czytelne niż a SELECT companyName FROM masterTable WHERE companyId = ?i rzadziej przypadkowo odpytujesz bzdury (np. SELECT companyName FROM masterTable WHERE employeeId = ?Nie byłoby to możliwe, gdy firmy i pracownicy są przechowywane w różnych tabelach). Jeśli chodzi o szybkość, dane z tabeli bazy danych są pobierane albo przez sekwencyjny odczyt pełnej tabeli, albo przez odczyt z indeksu. Oba są szybsze, jeśli tabela / indeks zawiera mniej danych, i tak jest w przypadku, gdy dane są przechowywane w różnych tabelach (a wystarczy przeczytać tylko jedną z tabel / indeksów).

2. Dlaczego encje jednego typu powinny być podzielone na sub-encje przechowywane w różnych tabelach?

Tutaj powodem jest przede wszystkim zapobieganie niespójnościom danych. Dzięki podejściu z pojedynczą tabelą w systemie zarządzania zamówieniami można przechowywać nazwę klienta, adres klienta i identyfikator produktu produktu zamówionego przez klienta jako jedną całość. Jeśli klient zamówił wiele produktów, w bazie danych będzie wiele wystąpień nazwy i adresu klienta. W najlepszym przypadku masz właśnie zduplikowane dane w bazie danych, co może nieco je spowolnić. Ale gorszym przypadkiem jest to, że ktoś (lub jakiś kod) popełnił błąd podczas wprowadzania danych, tak że firmy mają różne adresy w bazie danych. Samo to jest wystarczająco złe. Ale jeśli zapytasz o adres firmy na podstawie jej nazwy (npSELECT companyAddress FROM orders WHERE companyName = ? LIMIT 1) możesz po prostu arbitralnie zwrócić jeden z dwóch adresów i nawet nie zdawać sobie sprawy z niespójności. Ale za każdym razem, gdy uruchamiasz zapytanie, możesz faktycznie otrzymać inny adres, w zależności od tego, jak twoje zapytanie jest wewnętrznie rozwiązane przez DBMS. Prawdopodobnie spowoduje to uszkodzenie aplikacji w innym miejscu, a podstawowa przyczyna tego uszkodzenia będzie bardzo trudna do znalezienia.

Dzięki podejściu wielostołowemu zdajesz sobie sprawę, że istnieje funkcjonalna zależność od nazwy firmy do adresu firmy (jeśli firma może mieć tylko jeden adres), przechowujesz krotkę (companyName, companyAddress) w jednej tabeli (np. company) oraz krotka (productId, companyName) w innej tabeli (np order.). UNIQUEOgraniczenie na companystole może następnie dochodzić, że każda firma ma tylko jeden adres w bazie danych, tak aby żadna niekonsekwencja adresów firmowych może kiedykolwiek powstać.

Uwaga: w praktyce ze względów wydajnościowych prawdopodobnie wygenerowałbyś unikalną firmę dla każdej firmy i używałbyś jej jako klucza obcego zamiast bezpośredniego używania nazwy firmy. Ale ogólne podejście pozostaje takie samo.

Marzyciel
źródło
3

TL; DR - projektują bazę danych na podstawie tego , jak ich nauczono, gdy byli w szkole.

Mógłbym napisać to pytanie 10 lat temu. Trochę czasu zajęło mi zrozumienie, dlaczego moi poprzednicy tak zaprojektowali swoje bazy danych. Pracujesz z kimś, kto:

  1. Zdobył większość umiejętności projektowania baz danych przy użyciu Excela jako bazy danych lub
  2. Stosują najlepsze praktyki od momentu, gdy opuścili szkołę.

Nie podejrzewam, że to numer 1, ponieważ faktycznie masz numery identyfikacyjne w tabeli, więc założę się, że nr 2.

Po wyjściu ze szkoły pracowałem w sklepie, który korzystał z AS / 400 (alias IBM i). Znalazłem dziwne rzeczy w sposobie, w jaki projektowali swoje bazy danych, i zacząłem zalecać wprowadzanie zmian, aby śledzić sposób, w jaki nauczono mnie, jak projektować bazy danych. (Byłem wtedy głupi)

Cierpliwy starszy programista wyjaśnił mi, dlaczego tak się stało. Nie zmienili schematu, ponieważ spowodowałoby to uszkodzenie programów starszych ode mnie. Dosłownie kod źródłowy jednego programu miał datę utworzenia na rok przed moim narodzeniem. W systemie, nad którym pracowaliśmy, ich programy musiały zaimplementować całą logikę i operacje obsługiwane przez planer zapytań w bazie danych. (Możesz to zobaczyć, uruchamiając EXPLAIN na jednym ze swoich zapytań)

Był na bieżąco z technikami, które próbowałem wdrożyć, ale utrzymanie systemu było ważniejsze niż wprowadzanie zmian „ponieważ było to sprzeczne z tym, czego mnie nauczono”. Każdy nowy projekt, który rozpoczęliśmy, najlepiej wykorzystywał model relacyjny, jaki mogliśmy. Niestety inni programiści / konsultanci z tamtych czasów nadal projektowali swoje bazy danych, tak jakby pracowali z poprzednimi ograniczeniami tego systemu.


Kilka przykładów tego, co spotkałem, które nie pasowały do ​​modelu relacyjnego:

  • Daty były przechowywane jako liczby w dniach juliańskich, które wymagały dołączenia do tabeli dat, aby uzyskać rzeczywistą datę.
  • Denormalizowane tabele z sekwencyjnymi kolumnami tego samego typu (np. code1,code2, ..., code20)
  • Kolumny CHAR o długości NxM reprezentujące tablicę N ciągów o długości M.

Powody, dla których podano mi te decyzje projektowe, były oparte na ograniczeniach systemu, kiedy baza danych została po raz pierwszy zaprojektowana.

Daty - Powiedziano mi, że użycie funkcji daty (który miesiąc, dzień lub dzień tygodnia) do przetworzenia daty zajęło więcej czasu niż utworzenie tabeli każdej możliwej daty z wszystkimi tymi informacjami.

Kolumny sekwencyjne tego samego typu - środowisko programowe, w którym się znajdowały, pozwoliło programowi utworzyć zmienną tablicową na części wiersza. Był to łatwiejszy sposób na zmniejszenie liczby operacji odczytu.

Kolumny CHAR długości NxM - Łatwiej było wrzucić wartości konfiguracyjne do jednej kolumny, aby ograniczyć operacje odczytu plików.

Źle pomyślany przykład w języku C odpowiadający środowisku programistycznemu, które mieli:

#define COURSE_LENGTH 4
#define NUM_COURSES 4
#define PERIOD_LENGTH 2

struct mytable {
    int id;
    char periodNames[NUM_COURSES * PERIOD_LENGTH];  // NxM CHAR Column
    char course1[COURSE_LENGTH];
    char course2[COURSE_LENGTH];
    char course3[COURSE_LENGTH];
    char course4[COURSE_LENGTH];
};

...

// Example row
struct mytable row = {.id= 1, .periodNames="HRP1P2P8", .course1="MATH", .course2="ENGL", .course3 = "SCI ", .course4 = "READ"};

char *courses; // Pointer used to access the sequential columns
courses = (char *)&row.course1;


for(int i = 0; i < NUM_COURSES; i++) {

    printf("%d: %.*s -> %.*s\n",i+1, PERIOD_LENGTH, &row.periodNames[PERIOD_LENGTH * i], COURSE_LENGTH,&courses[COURSE_LENGTH*i]);
}

Wyjścia

1: HR -> MATH
2: P1 -> ENGL
3: P2 -> SCI
4: P8 -> CZYTAJ

Zgodnie z tym, co mi powiedziano, niektóre z nich uważano wówczas za najlepszą praktykę.

Core.B
źródło