Dane konfiguracyjne: tabela jednorzędowa vs. tabela para-nazwa-wartość


64

Załóżmy, że piszesz aplikację, którą może skonfigurować użytkownik. Do przechowywania tych „danych konfiguracyjnych” w bazie danych powszechnie stosuje się dwa wzorce.

  1. Tabeli jednorzędowy

      CompanyName  |  StartFullScreen  |  RefreshSeconds  |  ...
    ---------------+-------------------+------------------+--------
      ACME Inc.    |        true       |       20         |  ...
    
  2. Nazwa-wartość-pair stół

      ConfigOption   |   Value
    -----------------+-------------
     CompanyName     | ACME Inc.
     StartFullScreen | true (or 1, or Y, ...)
     RefreshSeconds  | 20
     ...             | ...
    

Obie opcje widziałem na wolności i obie mają oczywiste zalety i wady, na przykład:

  • Tabele jednorzędowe ograniczają liczbę możliwych opcji konfiguracji (ponieważ liczba kolumn w rzędzie jest zwykle ograniczona). Każda dodatkowa opcja konfiguracji wymaga zmiany schematu DB.
  • W tabeli par nazwa-wartość wszystko jest „sznurkowo wpisane” (musisz zakodować / zdekodować parametry logiczne / datę / itp.).
  • (wiele więcej)

Czy w społeczności programistów istnieje konsensus co do tego, która opcja jest lepsza?


2
Nie ma powodu, dla którego podejście „pionowe” nie może mieć różnych typów danych. Dodaj kolumnę int, float i text dla każdego wiersza. Zapisz / wczytaj z niego wartości za pomocą funkcji specyficznych dla typu, takich jak „SaveConfigInt ('field', n) '
GrandmasterB,

4
Pytanie to doskonale zadaje StackOverflow, a najlepsza odpowiedź daje zalety i wady obu podejść. stackoverflow.com/questions/2300356/…
Kevin

1
Podejście 3: Pojedyncza kolumna / pojedynczy wiersz z prostym formatem wymiany danych, takim jak JSON lub YAML. Łączy zalety obu podejść.
schlamar

co z użyciem tabeli jednorzędowej ze złożonymi danymi zawierającymi xml / json, takimi jak <config> <CompanyName> ACME Inc. </CompanyName> <StartFullScreen> true </StartFullScreen>20<RefreshSeconds></RefreshSeconds> </config> i zweryfikować obiekt w warstwie biznesowej?
Jan

1
@John: Dobry pomysł, jeśli potrzebne są struktury hierarchiczne. Jeśli tak nie jest, jest to tylko opcja 2 z dodatkową złożonością.
Heinzi

Odpowiedzi:


15

Osobiście wolę tabele jednorzędowe dla większości rzeczy. Chociaż prawdą jest, że jest mniej elastyczny, chyba że oczekujesz dynamicznego zachowania, w razie potrzeby możesz dodać dodatkowe kolumny później. W pewnym sensie jest to odpowiednik użycia słownika / mapy do przechowywania par nazwa-wartość w porównaniu do posiadania członków klasy podczas programowania. To prawda, że ​​nie jest to idealna metafora, ale wiele zalet i wad jest równoległych, gdy się nad tym zastanowić.

Czy użyłbyś słownika / mapy nad członkami klasy? Prawdopodobnie nie, chyba że masz powód, by sądzić, że ilość danych, które mają być reprezentowane, można w pełni dostosować, podobnie jak w przypadku tabeli par nazwa-wartość.


Co się stanie, jeśli dane, które mają być przechowywane, są zdefiniowane przez użytkownika? tzn. pomyśl o interfejsie użytkownika, w którym użytkownik może utworzyć „pole”, określając etykietę pola, rodzaj przechowywanych danych itp. Oznaczałoby to wykonanie instrukcji DDL z kodu. Czy nadal wybierałbyś opcję 1?
devanalyst

1
@devanalyst Nie, jeśli dane mogłyby się zmieniać z komponentu na komponent, nie ma sensu próbować tworzyć tabeli statycznej do jej reprezentowania. W takim przypadku lepiej byłoby użyć drugiej opcji.
Neil

12

Zwykle wybrałbym opcję 2, ALE miałbym wiele kolumn, aby wymusić typ danych

ConfigOption   |   textValue    |   DateValue   |   NumericValue

Opcja 1 ma tę dodatkową zaletę, że można bardzo łatwo „zamieniać” całe konfiguracje, dodając Activekolumnę.


Jeśli chcesz zezwolić na wyłączenie konfiguracji (dla opcji 1), przynajmniej uczyń to activatedOnznacznikiem czasu, abyś wiedział, kiedy został aktywowany. A jeśli wybierzesz opcję 2 ... co się stanie, jeśli w końcu przechowujesz wartości w wielu kolumnach (lub w wyroczni, gdzie (najwyraźniej) null i pusty ciąg są równoważne)?
Clockwork-Muse,

1
@ X-Zero, przechowywanie wielu konfiguracji jest zwykle wykonywane w celach testowych, ale znacznik czasu nie może zaszkodzić. Zadzwoń do Config Maintenance, aby uzyskać wartość, wiedziałby, którą kolumnę sprawdzić, jeśli naprawdę tego chcesz, możesz dodać kolumnę dla typu danych. Ale myślę, że to już koniec ...
Morons

5
schemat EATV (Entity-Attribute-Type-Value) łamie trzecią postać normalną; kolumna Typ jest tylko pośrednio związana z kluczem podstawowym tabeli, poprzez kolumnę Wartość, którą opisuje kolumna Typ. Ponadto dynamiczne przechowywanie i tworzenie instancji nie rozwiązuje wiele; jeśli metoda GetConfigValue () może zwrócić dowolny typ, musi zwrócić Object (lub w jakiś sposób otrzymać oczekiwany typ), który nadal musi być oceniany w czasie wykonywania.
KeithS

5
Za każdym razem, gdy opcja 1 została zaimplementowana w oprogramowaniu, które widziałem, musiała zostać przekonwertowana na opcję 2. Opcja 2 jest łatwiejsza do utrzymania z upływem czasu, po prostu wymaga więcej czasu, aby zaimplementować poprawnie za pierwszym razem. Opcja 1 jest szybka i łatwa do wdrożenia, ale konserwacja w czasie jest straszna, chyba że twoje oprogramowanie jest małe i nie ma szans na rozwój.
Jimmy Hoffa

8

Dla mnie to, czy wybierzesz jednorzędowy czy EAV, zależy od tego, jak chcesz je spożywać.

Moc EAV polega na tym, że można dodawać nowe dane bez zmian w strukturze. Oznacza to, że jeśli chcesz nową wartość konfiguracji, po prostu dodaj ją do tabeli i wyciągnij w żądanym miejscu w kodzie, i nie musisz dodawać nowego pola do domeny, schematu, mapowania, zapytań DAL itd.

Jego wadą jest to, że ma tylko najsłabszą strukturę, co wymaga od ciebie pesymistycznego traktowania danych. Każde użycie dowolnej wartości konfiguracyjnej musi przewidywać, że wartość nie będzie obecna lub nie będzie miała odpowiedniego formatu i będzie zachowywać się odpowiednio, gdy nie będzie. Wartość konfiguracji nie może być przetwarzalna na wartość double, int lub char. Może być zerowy. może nie być wcale wiersza dla wartości. Sposoby obejścia tego zwykle wymagają istnienia pojedynczej prawidłowej wartości „domyślnej” dla wszystkich wartości konfiguracyjnych określonego typu kodu ( wyjątkowo rzadko; częściej wartość domyślna jest tak samo problematyczna przy zużyciu kodu jak żadna) lub prowadzić zakodowany słownik wartości domyślnych (który musi się zmieniać za każdym razem, gdy dodawana jest nowa kolumna, co sprawia, że ​​podstawową zaletą pamięci EAV jest dość dyskusyjny).

Pojedynczy szeroki rząd jest wręcz przeciwny. Odwzorowujesz go na pojedyncze wystąpienie obiektu konfiguracji z polem / właściwością dla każdej istniejącej wartości konfiguracji. Wiesz dokładnie, jakiego typu powinny być te wartości w czasie kompilacji, i „szybko się nie udaje” w DAL, jeśli kolumna konfiguracji nie istnieje lub nie ma wartości odpowiedniego typu, co daje jedno miejsce na wychwytywanie wyjątków w sprawie problemów z odzyskiwaniem konfiguracji / nawadnianiem.

Główną wadą jest to, że dla każdej nowej wartości wymagana jest zmiana strukturalna; nowa kolumna DB, nowa kolumna w DAL (mapowanie lub zapytania SQL / SP), nowa kolumna domeny, wszystkie niezbędne do poprawnego przetestowania użycia.

Właściwą sytuacją do zastosowania któregokolwiek z nich jest sytuacja, w której wady są łagodzone. Dla mnie większość sytuacji związanych z kodowaniem konfiguracji wymaga implementacji jednorzędowej. Wynika to głównie z tego, że jeśli wprowadzasz zupełnie nową wartość konfiguracji, która rządzi zachowaniem jakiejś części twojego programu, musisz już zmienić kod, aby użyć nowej wartości konfiguracji; dlaczego nie wyskoczyć do obiektu config i dodać wartość, która ma być użyta?

Krótko mówiąc, schemat EAV do przechowywania konfiguracji tak naprawdę nie rozwiązuje problemu, który ma rozwiązać, a większość obejść problemów, które przedstawia, narusza DRY.


3

Powiedziałbym, że szczególnie w przypadku wartości konfiguracyjnych - przejdź do jednego wiersza. Jak często te kolumny i tak będą się zmieniać, chyba że aktualnie przechodzisz programowanie?

Prawdopodobnie najlepiej zabezpieczyć typ danych wartości niż kod rozszerzalności, którego prawdopodobnie nie będziesz mieć w czasie przestoju między dużymi (r) wydaniami. Poza tym dodanie lub usunięcie pojedynczej kolumny jest najłatwiejszą dostępną migracją. Nie widzę bólu głowy podczas tworzenia nowej opcji konfiguracji.

Ponadto powiedziałeś, że „użytkownicy” mogą konfigurować te opcje bez ograniczenia. Czy są to konfiguracje dla poszczególnych użytkowników? Jeśli tak, będę jeszcze mocniej argumentować, że opcje konfiguracji powinny znajdować się w kolumnach - jeden wiersz na użytkownika. Zaoszczędzi to później wielu problemów związanych z konserwacją.


2

Jeśli Twoi klienci mogą przetwarzać fragmenty JSON (to nie tylko tablice i słowniki, ale także zwykłe ciągi, liczby, booleany, wartości null), możesz mieć tabelę z wieloma wierszami z nazwą opcji i wartością ciągu zawierającego JSON. Pozwala to również przechowywać wartości strukturalne, a kod do ich przetwarzania powinien już tam być.

Jeśli Twoi klienci nie mogą przetwarzać fragmentów JSON, zdobądź nowych klientów.


1

Jednorzędowe zalety: dobrze zdefiniowane. Minusy: Zmiana konfiguracji może być uciążliwa. Migracje DB itp.

Zalety wartości encji: Super elastyczny, wspiera ewolucję konfiguracji. Minusy: integralność referencyjna? Więcej kontroli w kodzie, aby sprawdzić, czy właściwość istnieje, zanim będziesz mógł cokolwiek na niej zrobić.

Przyjąłbym podejście 2 wspierane przez nierelacyjną bazę danych, taką jak Mongo. Jeśli jest coś, czego możesz być pewien, to zmiana.


1

Używać obu!

Posortuj, które opcje mogą mieć wiele instancji i jakie są ogólne.

Tabela jednorzędowa (konfiguracje)

  id  |  company_name  |  start_fullscreen  |  refresh_seconds  |  ...
------+----------------+--------------------+-------------------+-------
  4   |  ACME Inc.     |  true              |  20               |  ...

Tabela para nazwa-wartość (opcje)

  name             |  value          | update_time  
-------------------+-----------------+--------------
  generic_option_1 |  Option 1 Value | timestamp    
  generic_option_2 |  Option 2 Value | timestamp    
  generic_option_3 |  Option 3 Value | timestamp    
  configuration    |  4              | timestamp    
  ...              |  ...            | ...          

Myślę, że to jest bardziej elastyczne.

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.