Zasadniczo, czy uważa się za złą praktykę zezwalanie na pola tworzone przez użytkowników w bazie danych dla aplikacji WWW?
Na przykład robię dla mojej żony aplikację internetową do inwentaryzacji domu i będzie chciała zdefiniować własne pola dla różnych przedmiotów. Planowałem pozwolić jej na tworzenie kategorii przedmiotów i dodawanie „funkcji” do tych kategorii. Funkcje byłyby po prostu kluczem / wartością przechowywaną jako ciągi znaków. W ten sposób, gdyby miała kategorię o nazwie „Audio CD”, mogłaby dodawać funkcje do rzeczy takich jak „artysta”, „utwory” itp. Ale w innej kategorii, jak „meble”, mogłaby dodawać funkcje do rzeczy takich jak „materiał” „(drewno, plastik itp.). Wtedy dowolny element może należeć do jednej (lub wielu) kategorii, dodając te funkcje do elementu.
Widzę problemy, w których wyszukiwanie za pomocą tych funkcji wymaga porównań ciągów, nie ma sprawdzania poprawności danych itp. Zgodnie ze zwinną metodologią może lepiej byłoby, gdyby wymyśliła nowe kategorie i atrybuty, a ja po prostu musiałbym stworzyć nowe tabele Jak idziemy. W moim przykładzie jest to mała baza użytkowników (2 z nas), a liczba utworzonych rekordów byłaby niewielka, więc nie jest tak źle.
Ogólnie rzecz biorąc, jak ludzie radzą sobie z czymś takim w „prawdziwym życiu”?
Odpowiedzi:
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ę:
Dzieje się tak po dodaniu kilku atrybutów. Zamiast
attr1
udawać, że czytaartist
lubtracks
lubgenre
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:
Masz swoje rzeczy z podstawowymi polami. Masz jeszcze dwa tabele. Jeden z atrybutami. Każde pole jest wierszem w
attr
tabeli. I jestthing_attr
jeszcze para kluczy obcych odnoszących się dothing
stołu iattr
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ć.)
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:
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.hasdate
pole do przechowywania znacznika czasu, zostało już zdefiniowane jakohasdate
boolean dla udanego dopasowania ... a aktualizacja zostanie zerwana.varchar2
vstext
i tym podobne. Twój kod, aby dodać kolumnę, działałby na MySQL, ale nie w Postgres, Oracle ani SQL Server.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.
źródło
Robienie tego dobrze jest trudne.
W przypadku jednorazowej aplikacji, takiej jak to, co planujesz, możesz oczywiście po prostu dodać kolumnę dla każdego pola i podać interfejs użytkownika, który sprawia, że definiowanie pola przez nieprzeszkolonych użytkowników jest bezpieczniejsze niż podawanie wiersza poleceń SQL. Możesz też zastosować się do przerażającego wzorca Entity-Attribute-Value , który jest klasyczną, choć nieco przerażającą odpowiedzią na tego rodzaju problem. Budowanie interfejsu użytkownika w celu zdefiniowania pól EAV jest zwykle znacznie bardziej skomplikowane niż w przypadku kolumn bazy danych, a zapytania mogą stać się dość włochate, ale w przypadku dużej liczby pól ( tj. Schematów o bardzo rzadkiej macierzy) może to być jedyny sposób na uzyskanie praca wykonana.
źródło
Ostatnio spotkałem coś podobnego.
Zrobiłem 2 stoły.
On jest wszystkimi twoimi przedmiotami. U ustawiłeś jego nazwę.
I typ tego obiektu: - dla mnie dostępne typy to ekwipunek, ekwipunek_wyprzedaż, biuro.
I zwykła konfiguracja była n przedmiotów lub ekwipunku, który jest również dzieckiem biura i użyłem tabeli łączenia, aby połączyć obiekty ze sobą
Tabela ustawień zawiera każdą nazwę pola dla tego konkretnego typu obiektu i wartość.
Przykładowe właściwości biura
Lokalizacja, telefon, godziny pracy
I dla przedmiotów
Itd, wszystkie te właściwości są wymuszane przez Twój model i zapisywane w tabeli ustawień jako osobne wiersze (ale używaj zamiany zamiast wstawiania, aby uniknąć wielu wierszy dla tego samego pola)
Kiedy więc chcę mieć biuro, ładuję je łatwo ze wszystkimi jego relacjami i ustawieniami, w których znajdują się ustawienia object_I'd (żądane obiekty)
Następnie przestawiam wszystkie wiersze z ustawień i to wszystko.
W przypadku, gdy chciałem, aby ustawienie było specyficzne dla elementu w ekwipunku (nie globalnym), ustawiam object_I'd = Chciałbym z tabeli relacji object_objects i ustawiam settings.type = zestaw_konfiguracji
Mam nadzieję, że rozumiesz, co mam na myśli, że spróbuję sformatować odpowiedź, gdy dojdę do laptopa
źródło
Nie, to nie jest zła praktyka. To jest dość powszechne. W kategoriach OO nazywa się to dziedziczeniem. Masz inwentarz klasy podstawowej i dwie odziedziczone klasy AudioCD i meble.
Musisz zdecydować, w jaki sposób zapasyItem, AudioCD i meble są przechowywane w bazie danych.
Jeśli najważniejsze jest dla Ciebie łatwe zapytanie, a db-space / normalizacja nie ma znaczenia, zaimplementuj schemat „Tabela według hierarchii”.
Jeśli najważniejsza jest dla Ciebie przestrzeń / normalizacja, a bardziej skomplikowane zapytania nie stanowią problemu, zaimplementuj schemat „Tabela według typu”.
Aby uzyskać więcej informacji, zobacz dziedziczenie dotnet table-per-typ-vs-table-hierarchy-dziedziczenie lub hibernacja java .
źródło