Czy marnowanie nowej tabeli bazy danych jest niepotrzebne zamiast używania typu danych enum?


38

Załóżmy, że mam 4 rodzaje usług, które oferuję (raczej nie zmieniają się często):

  • Testowanie
  • Projekt
  • Programowanie
  • Inny

Załóżmy, że mam 60-80 faktycznych usług, z których każda należy do jednej z powyższych kategorii. Na przykład „usługa” może być „Programem testowym z wykorzystaniem techniki A” i jest typu „Testowanie”.

Chcę zakodować je w bazie danych. Wymyśliłem kilka opcji:

Opcja 0:

Użyj VARCHARbezpośrednio, aby zakodować typ usługi bezpośrednio jako ciąg

Opcja 1:

Użyj bazy danych enum. Ale wyliczenie jest złe

Opcja 2:

użyj dwóch tabel:

service_line_item (id, service_type_id INT, description VARCHAR);
service_type (id, service_type VARCHAR);

Mogę nawet cieszyć się integralnością referencyjną:

ALTER service_line_item 
    ADD FOREIGN KEY (service_type_id) REFERENCES service_type (id);

Brzmi dobrze, tak?

Ale wciąż muszę kodować rzeczy i radzić sobie z liczbami całkowitymi, tj. Podczas zapełniania tabeli. Albo muszę tworzyć skomplikowane konstrukcje programistyczne lub konstrukcje DB podczas zapełniania tabeli lub zajmowania się nią. Mianowicie, ŁĄCZĄ się przy bezpośredniej obsłudze bazy danych lub tworzeniu nowych obiektowych obiektów po stronie programowania i upewnianiu się, że operuję nimi poprawnie.

Opcja 3:

Nie używaj enum, nie używaj dwóch tabel, ale po prostu użyj kolumny liczb całkowitych

service_line_item (
    id,
    service_type INT,        -- use 0, 1, 2, 3 (for service types)
    description VARCHAR
);

To jest jak „fałszywy wyliczenie”, które wymaga większego narzutu po stronie kodu, na przykład wiedząc o tym {2 == 'Programming'}i odpowiednio sobie z tym radząc.

Pytanie:

Obecnie zaimplementowałem go przy użyciu Opcji 2 , kierując się koncepcjami

  1. nie używaj enum (opcja 1)
  2. unikaj używania bazy danych jako arkusza kalkulacyjnego (opcja 0)

Ale nie mogę nie poczuć, że wydaje mi się to marnotrawstwem w zakresie programowania i ogólnych kosztów poznawczych - muszę być świadomy dwóch tabel i radzić sobie z dwiema tabelami w porównaniu do jednej.

Patrzę na „mniej marnotrawny sposób” Option 3. IT jest lżejsze i do działania wymaga zasadniczo tych samych konstrukcji kodu (z niewielkimi modyfikacjami, ale złożoność i struktura są w zasadzie takie same, ale z jedną tabelą)

Sądzę, że idealnie nie zawsze jest to marnotrawstwo i istnieją dobre przypadki dla obu opcji, ale czy istnieją dobre wytyczne, kiedy należy użyć opcji 2, a kiedy opcji 3?

Gdy są tylko dwa typy (binarne)

Aby dodać nieco więcej do tego pytania ... w tym samym miejscu, mam opcję binarną usługi „Standard” lub „Wyjątek”, która może dotyczyć elementu zamówienia. Zakodowałem to przy użyciu Opcji 3 .

Zdecydowałem się nie tworzyć nowej tabeli, aby przechowywać wartości {„Standard”, „Wyjątek”}. Więc moja kolumna zawiera po prostu {0, 1} i nazwa mojej kolumny jest wywoływana exception, a mój kod wykonuje tłumaczenie {0, 1} => {STANDARD, EXCEPTION}( z którego kodowałem jako stałe w języku programowania)

Jak dotąd nie podoba mi się ten sposób ... (nie podoba się opcja 2 ani opcja 3). Uważam, że opcja 2 jest lepsza od 3, ale z większym obciążeniem i nadal nie mogę uniknąć kodowania rzeczy jako liczb całkowitych bez względu na to, której opcji użyję z 2 i 3.

ORM

Aby dodać kontekst, po przeczytaniu odpowiedzi - zacząłem ponownie używać ORM (ostatnio), w moim przypadku Doctrine 2. Po zdefiniowaniu schematu DB za pomocą Adnotacji, chciałem zapełnić bazę danych. Ponieważ cały mój zestaw danych jest stosunkowo niewielki, chciałem spróbować użyć konstrukcji programistycznych, aby zobaczyć, jak to działa.

Najpierw zapełniłem service_types, a potem service_line_items, ponieważ istniała lista z rzeczywistego arkusza kalkulacyjnego. Tak więc rzeczy takie jak „standardowy / wyjątek” i „Testowanie” są ciągami w arkuszu kalkulacyjnym i muszą być zakodowane w odpowiednich typach przed zapisaniem ich w DB.

Znalazłem odpowiedź SO: Czego używasz zamiast ENUM w doctrine2? , co sugerowało, że nie należy używać konstrukcji enum DB, ale użyć INTpola i zakodować typy przy użyciu konstrukcji „const” języka programowania.

Ale jak wskazano w powyższym pytaniu SO, mogę uniknąć bezpośredniego użycia liczb całkowitych i użyć konstrukcji językowych - stałych - po ich zdefiniowaniu ...

Ale nadal ... bez względu na to, jak to zmienisz, jeśli zaczynam stringjako typ, muszę najpierw przekonwertować go na odpowiedni typ, nawet jeśli używam ORM.

Więc jeśli powiedzmy $str = 'Testing';, nadal muszę mieć blok, który robi coś takiego:

switch($str):
{ 
    case 'Testing':  $type = MyEntity::TESTING; break;
    case 'Other':    $type = MyEntity::OTHER; break;
}

Dobrą rzeczą jest to, że nie masz do czynienia z liczbami całkowitymi / liczbami magicznymi [zamiast tego, mając do czynienia z zakodowanymi stałymi wielkościami], ale złą rzeczą jest to, że nie możesz automatycznie wciągać i wyprowadzać rzeczy z bazy danych bez tego kroku konwersji, do mojego wiedza, umiejętności.

I to właśnie miałem na myśli, mówiąc: „wciąż muszę kodować i radzić sobie z liczbami całkowitymi”. (Oczywiście, teraz, po komentarzu Ocramiusa, nie będę musiał zajmować się bezpośrednio liczbami całkowitymi, ale zajmować się nazwanymi stałymi i pewną konwersją do / z stałych, w razie potrzeby).


9
Cokolwiek robisz, nie rób # 3. Utrzymujący ją psychopata będzie musiał nieustannie odkrywać, co oznaczają te magiczne liczby. Jeśli to zrobisz, lepiej mieć nadzieję, że nie wiedzą, gdzie mieszkasz. blog.codinghorror.com/coding-for-violent-psychopaths
RubberDuck

7
Podoba mi się opcja 2. Jeśli nie podoba Ci się mnożenie tabel odnośników, użyj jednej tabeli i dodaj kolumnę „typ odnośnika”. Ale tak, tworzenie tabeli odnośników jest „standardowym” sposobem na to, ponieważ pozwala robić fajne rzeczy, takie jak łatwe wypełnianie menu w interfejsie użytkownika.
Robert Harvey

Nie używaj „EDYTUJ” w swoich postach tutaj; nie jesteśmy forum. Każdy post Stack Exchange zawiera już szczegółową historię edycji, którą każdy może wyświetlić.
Robert Harvey

jeśli nie mogę skorzystać z EDIT, co mam użyć?
Dennis

Po prostu edytuj post i spraw, by wyglądał naturalnie, tak jak już to zrobiłem. Zobacz historię edycji, aby przejrzeć zmiany.
Robert Harvey

Odpowiedzi:


35

Opcja # 2, wykorzystująca tabele referencyjne, jest standardowym sposobem na zrobienie tego. Jest używany przez miliony programistów i jest znany z działania. Jest to wzór , więc każdy, kto patrzy na twoje rzeczy, natychmiast wie, co się dzieje. Istnieją biblioteki i narzędzia, które działają na bazach danych, dzięki czemu oszczędzasz dużo pracy, która poradzi sobie z nią poprawnie. Korzyści z korzystania z niego są niezliczone.

Czy to marnotrawstwo? Tak, ale tylko nieznacznie. Każda na wpół przyzwoita baza danych zawsze przechowuje w pamięci podręcznej tak często łączone małe tabele, więc marnotrawstwo jest na ogół niezauważalne.

Wszystkie inne opcje, które opisałeś, są ad hoc i hacky, w tym MySQL enum, ponieważ nie jest częścią standardu SQL. (Poza tym, do bani enumjest implementacja MySQL, a nie sam pomysł. Nie miałbym nic przeciwko postrzeganiu go pewnego dnia jako części standardu.)

Twoja ostatnia opcja # 3 z użyciem zwykłej liczby całkowitej jest szczególnie hackerska. Otrzymujesz najgorsze ze wszystkich światów: brak integralności referencyjnej, brak nazwanych wartości, brak ostatecznej wiedzy w bazie danych o tym, co oznacza wartość, po prostu arbitralne liczby całkowite wyrzucane wszędzie. Dzięki temu tokenowi równie dobrze możesz wyjść z używania stałych w kodzie i zamiast tego zacząć używać wartości zakodowanych na stałe. circumference = radius * 6.28318530718;. Co ty na to?

Myślę, że powinieneś ponownie zbadać, dlaczego uważasz, że tabele referencyjne są uciążliwe. O ile mi wiadomo, nikt inny nie uważa ich za uciążliwe. Czy to możliwe, że nie używasz odpowiednich narzędzi do pracy?

Twoje zdanie na temat „kodowania rzeczy i radzenia sobie z liczbami całkowitymi” lub „tworzenia skomplikowanych konstrukcji programistycznych” lub „tworzenia nowych obiektowych obiektów po stronie programistycznej” mówi mi, że być może próbujesz wykonać relację obiektową mapowanie (ORM) w locie rozproszone w kodzie aplikacji, lub w najlepszym wypadku możesz próbować rzucić własny mechanizm obiektowo-relacyjnych mapowań, zamiast używać istniejącego narzędzia ORM dla zadania, takiego jak Hibernacja. Wszystkie te rzeczy są proste dzięki Hibernacji. Nauczenie się go zajmuje trochę czasu, ale gdy się go nauczysz, możesz naprawdę skupić się na rozwijaniu aplikacji i zapomnieć o drobiazgowej mechanice reprezentowania danych w bazie danych.

Wreszcie, jeśli chcesz ułatwić sobie życie, pracując bezpośrednio z bazą danych, możesz zrobić co najmniej dwie rzeczy, o których mogę teraz myśleć:

  1. Twórz widoki, które łączą twoje główne tabele z dowolnymi tabelami referencyjnymi, do których się odnoszą, tak aby każdy wiersz zawierał nie tylko identyfikatory referencyjne, ale także odpowiadające im nazwy.

  2. Zamiast używać identyfikatora liczby całkowitej w tabeli referencyjnej, użyj kolumny CHAR (4) z 4-literowymi skrótami. Tak więc identyfikatory twoich kategorii to „TEST”, „DSGN”, „PROG”, „OTHR”. (Ich opisy pozostaną oczywiście odpowiednimi angielskimi słowami.) Będzie trochę wolniej, ale zaufaj mi, nikt tego nie zauważy.

Wreszcie, gdy są tylko dwa typy, większość ludzi po prostu używa kolumny boolowskiej. Tak więc ta kolumna „standard / wyjątek” zostałaby zaimplementowana jako wartość logiczna i nazwana byłaby „IsException”.


3
Nawiasem mówiąc , Postgres ma również typy wyliczeniowe . Są proste i nic specjalnego, co pozwala na użycie czytelnego ciągu jako wartości, ale pod maską można zastosować bardziej wydajną liczbę całkowitą.
Kat

Co z przypadkiem, w którym dane są konsekwentnie powtarzane, ale nie są zbędne (np. Nie spowoduje anomalii aktualizacji / wstawiania / usuwania)? Na przykład płeć osoby (mało prawdopodobne, aby wprowadziła nowe typy danych, nigdy nie będzie musiała zmieniać nazwy płci itp.)
Adam Thompson

To: ponieważ w końcu okaże się, że potrzebujesz „środowiska akceptacji” i twoje niezmienne wyliczenia muszą zostać zmienione.
Pieter B

3

Opcja 2 ze stałymi lub wyliczeniami na końcu programowania.
Mimo że powiela wiedzę, naruszając zasadę pojedynczego źródła prawdy, możesz sobie z tym poradzić, stosując technikę szybkiego działania . Gdy system się ładuje, sprawdza, czy w bazie danych istnieją wartości wyliczone lub stałe. Jeśli nie, system powinien zgłosić błąd i odmówić załadowania. Naprawienie tego błędu będzie na ogół tańsze niż później, gdy mogło się zdarzyć coś poważniejszego.


0

Nic nie stoi na przeszkodzie, aby używać [krótkich] ciągów znaków jako kluczy, więc nadal możesz mieć czytelność nazw w swoich tabelach i nie uciekać się do bezsensownego kodowania liczb zastępczych. Nadal powinieneś mieć oddzielną tabelę opisującą typy usług, na wszelki wypadek, że, powiedzmy, twoja aplikacja jest międzynarodowa!

Użytkownicy mogą zobaczyć twoje cztery kategorie w swoim własnym języku, ale tabele bazy danych nadal zawierają wartości, które możesz odczytać - i żadna z nich nie wymaga żadnej struktury bazy danych ani zmian kodu!

table service_type 
( id VARCHAR 
, name VARCHAR 
  primary key ( id ) 
);
table service_line_item 
( id 
, service_type VARCHAR 
, description VARCHAR
  foreign key ( service_type ) references service_type ( id )
);

select * from service_type ; 

+-------------+----------------+
| id          | name           |
+-------------+----------------+
| Testing     | Testen         |
| Design      | Design         | 
| Programming | Programmierung |
| Other       | Andere         |
+-------------+----------------+

lub, dla twoich francuskich klientów ...

update services_types set name = 'Essai'         where id = 'Testing'; 
update services_types set name = 'Conception'    where id = 'Design'; 
update services_types set name = 'Programmation' where id = 'Programming'; 
update services_types set name = 'Autre'         where id = 'Other'; 
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.