Kiedy zaczynasz docierać do „pól zdefiniowanych przez użytkownika”, co często znajduje się w modułach do śledzenia błędów, zarządzanie zasobami klientów i podobne narzędzia biznesowe polegają na tym, że nie są one wspierane tabelą zawierającą pola bajillionowe (jeśli tak, to prawdopodobnie jest to problem jego).
Zamiast tego znajdziesz projekty tabeli wartości atrybutów encji i powiązane narzędzie administracyjne do zarządzania prawidłowymi atrybutami.
Rozważ następującą tabelę:
+ -------------- +
| rzecz |
| -------------- |
| id |
| typ |
| desc |
| attr1 |
| attr2 |
| attr3 |
| attr4 |
| attr5 |
+ -------------- +
Dzieje się tak po dodaniu kilku atrybutów. Zamiast attr1
udawać, że czyta artist
lub tracks
lub genre
lub cokolwiek atrybuty rzecz ma. A zamiast 5, co gdyby było 50. Oczywiście nie da się tego zarządzać. Wymaga również aktualizacji modelu i ponownego wdrożenia aplikacji w celu obsługi nowego pola. Nieidealny.
Teraz rozważ następującą strukturę tabeli:
+ -------------- + + --------------- + + ------------- +
| rzecz | | thing_attr | | attr |
| -------------- | | --------------- | | ------------- |
| id | <--- + | thing_id (fk) | +> | id |
| typ | | attr_id (fk) | + - + | nazwa |
| desc | | wartość | | |
+ -------------- + + --------------- + + ------------- +
Masz swoje rzeczy z podstawowymi polami. Masz jeszcze dwa tabele. Jeden z atrybutami. Każde pole jest wierszem w attr
tabeli. I jest thing_attr
jeszcze para kluczy obcych odnoszących się do thing
stołu i attr
stołu. I to ma pole wartości, w którym przechowuje się dowolną wartość pola dla tego bytu.
A teraz masz strukturę, w której tabela attr może być aktualizowana w czasie wykonywania, a nowe pola można dodawać (lub usuwać) w locie bez znaczącego wpływu na ogólną aplikację.
Zapytania są nieco bardziej złożone, a sprawdzanie poprawności również staje się bardziej złożone (albo funky procedury składowane, albo cała strona klienta). To kompromis w projektowaniu.
Rozważ także sytuację, w której pewnego dnia musisz przeprowadzić migrację i wrócisz do aplikacji, aby stwierdzić, że istnieje teraz około pół tuzina więcej atrybutów niż schemat, który pierwotnie rozpowszechniłeś. To sprawia, że brzydkie migracje i aktualizacje, w przypadku których tabela wartości atrybutu jednostki, gdy jest używana poprawnie, może być czystsza. (Nie zawsze, ale może być.)
Czy są jakieś wady modyfikowania schematu w czasie wykonywania? Jeśli użytkownik uważa, że rzecz wymaga nowego atrybutu, wystarczy dynamicznie dodać kolumnę do tabeli?
Jeśli pracujesz z odpowiednim smakiem bazy danych nosql, prawdopodobnie mógłbyś to zrobić (zauważ, że odpowiedni smak nosql do tego prawdopodobnie byłby magazynem klucz-wartość, który jest, no cóż, tabelą EAV dla relacyjnych opisanych powyżej) bez większych problemów. Jednak zawiera wszystkie kompromisy dotyczące nosql, które zostały szczegółowo opisane w innym miejscu.
Jeśli zamiast tego pracujesz nad relacyjną bazą danych - musisz mieć schemat. Dynamiczne dodanie kolumny oznacza, że niektóre podzbiory następujących rzeczy są prawdziwe:
- Robisz programowanie dla baz danych. Zamiast czystego mapowania tej kolumny do tego pola za pomocą ładnej ORM, prawdopodobnie robisz takie rzeczy,
select *
a następnie robisz skomplikowany kod, aby dowiedzieć się, co to właściwie są dane (zobacz wynik w Javie ResultSetMetaData ), a następnie przechowywanie tego w mapie ( lub inny typ danych - ale niezbyt ładne pola w kodzie). To z kolei rzuca sporo bezpieczeństwa tekstowego i literowego, które masz dzięki tradycyjnemu podejściu.
- Prawdopodobnie porzuciłeś ORM. Oznacza to, że piszesz surowy kod SQL dla całego kodu, zamiast pozwolić systemowi wykonać pracę za Ciebie.
- Zrezygnowałeś z robienia czystych aktualizacji. Co się stanie, gdy klient doda pole z jedną nazwą, którego używa także Twoja następna wersja? W witrynie matchmakingu uaktualnienie, które chce dodać
hasdate
pole do przechowywania znacznika czasu, zostało już zdefiniowane jako hasdate
boolean dla udanego dopasowania ... a aktualizacja zostanie zerwana.
- Ufasz, że klient nie zepsuje systemu, używając jakiegoś słowa zastrzeżonego, które również łamie twoje zapytania… gdzieś.
- Związałeś się z jedną marką bazy danych. DDL z różnych baz danych jest inna. Typy baz danych są najprostszym tego przykładem.
varchar2
vs text
i tym podobne. Twój kod, aby dodać kolumnę, działałby na MySQL, ale nie w Postgres, Oracle ani SQL Server.
- Czy ci zaufać klientowi rzeczywiście dodać dane dobrze ? Jasne, EAV jest daleki od ideału, ale teraz masz jakieś przerażające, niejasne nazwy tabel, których twórca nie dodał, z niewłaściwym typem indeksu (jeśli istnieje), bez żadnych ograniczeń w kodzie, w których trzeba być i tak dalej.
- Użytkownik uruchomił aplikację, nadając uprawnienia do modyfikacji schematu. Małe tabele upuszczania Bobby'ego nie są możliwe, gdy jesteś ograniczony do SQL zamiast DDL (na pewno możesz to zrobić
delete * from students
, ale naprawdę nie możesz zepsuć bazy danych w zły sposób). Liczba rzeczy, które mogą pójść nie tak z dostępem do schematu po wypadku lub złośliwej aktywności, gwałtownie rośnie.
To naprawdę sprowadza się do „nie rób tego”. Jeśli naprawdę tego chcesz, skorzystaj ze znanego wzorca struktury tabeli EAV lub bazy danych całkowicie poświęconej tej strukturze. Nie pozwól ludziom tworzyć dowolnych pól w tabeli. Bóle głowy po prostu nie są tego warte.