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?
źródło
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!Odpowiedzi:
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.
źródło
Korzystanie z dedykowanego oprogramowania do zarządzania bazami danych może być znacznie łatwiejsze (przepraszam; nie mogłem się oprzeć).
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.
źródło
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:
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.
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).
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.
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!
źródło
WHERE
klauzuli. 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.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ę):
i baza danych z pojedynczą tabelą wyglądałaby tak
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
Teams
stołu lubTeamsAndGames
stoł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 przypadkuTeams
stołu jest to dość proste. Wprowadziłem nowy zespół, baza danych aktualizuje indeks. Ale po coTeamsAndGames
? To samo odnosi się doTeams
przykł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
Games
stołu doTeams
stołu, aby określić, które drużyny grają w grę. Zakładając, że nie dopuszczam do zerowania kolumnHomeTeamId
iAwayTeamId
, 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ć
GameHomeTeamId
identyfikator 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
TeamName
lubTeamHomeCity
. 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ę bezTeamName
. 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.
źródło
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:
IngCompanyID
IvrCompanyName
). Aktualizacja danych podstawowych może wymagać aktualizacji znacznie większej liczby wierszy niż w znormalizowanym schemacie.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.
źródło
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ż aSELECT 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 (np
SELECT 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 (nporder
.).UNIQUE
Ograniczenie nacompany
stole 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.
źródło
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:
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:
code1,code2, ..., code20
)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:
Zgodnie z tym, co mi powiedziano, niektóre z nich uważano wówczas za najlepszą praktykę.
źródło