Datenbankschema - Verbesserungsvorschläge

DrCox1911

Lt. Junior Grade
Registriert
Juni 2018
Beiträge
503
Nabend zusammen,

ich bin gerade an der Planung eines privaten Projekts und habe mir mal ein paar Gedanken zur Datenbank gemacht.
Da es in der SW-Entwicklung ja immer zig Wege gibt und Input von Außen/anderen Entwicklern immer Goldwert ist, wollte ich euch mal nach Optimierungen fragen.

Umgesetzt wird es mittels .Net Core 5.

Vorab ein bisschen was zum Programm selbst, das dann hinten "rausfallen" soll:
Allgemeine Struktur:
  • DB (PostgreSQL, mit EF Core)
  • API
  • Web-UI (Blazor, später evtl. Angular)
Feature-Set (nur die groben Eckpunkte, habe das natürlich schon näher definiert, ist denke ich aber jetzt für die DB nicht so ausschlaggebend):
  • Scannen von Movie und TV-Shows in DB
  • MediaInfo der Movie und TV-Shows in DB
  • MetaData (wie z.B. Cover, ...) für Movie und TV-Shows in DB
  • MetaData kann flexibel erweitert werden (deshalb in der DB die XMetaDataType Tabellen mit dem Regex zur Dateiauswahl)
  • Unterschiedliche Handler für MetaData (z.B. Bilder anzeigen, Kodi-NFO parsen, ...)

Das ist jetzt nur grob das, was die DB betrifft. Das soll dann natürlich übersichtlich in der Web-UI dargestellt werden.
Ich habe hier jetzt kein "echtes" DB-Schema gezeichnet, sondern direkt an Entity Framwork Core angelehnt, da ich Code-First machen werde.

Das ganze Projekt soll mir später einen schönen Überblick über meine Kodi-Sammlung geben, aber auch vorrangig als Übung dienen. Will dieses Projekt nämlich später auch in Docker-Containern laufen lassen und einfach ein paar Dinge lernen und ausprobieren.

Da ich in der Arbeit nicht der DB-Designer bin, hoffe ich auf viele Tipps und Vorschläge, wie ihr das machen würdet. Bin da ziemlicher Neuling drin (daher stimmen die Pfeile wahrscheinlich auch nicht 100% von der Nomenklatur her).

DB-schema_v1.png
 
Verbesserungsvorschläge...hmm.
Also ich bin zwar in der Arbeit auch kein DB Entwickler. Habs aber Mal gelernt und auch doch irgendwie einige Projekte für den professionellen Einsatz gebaut. Mein Eindruck ist, dass Du eine viel zu komplexe Struktur baust. Gerade in den Tabellen die 1:1 verknüpft sind (zB MetaSata : MetaDataType) sehe ich (aus der Darstellung) keinen Mehrwert diese in eine andere Tabelle auszulagern.
Wenn Du das aus "Lerngründen" so machen willst - mach es halt. Aber "verkomplizieren" ist eigentlich genau das, was man bei der DB-Entwicklung tunlichst vermeiden sollte :)
 
keine Guid verwenden bei normalen Datenbanken, die sind nicht sortierbar im Normalfall, damit steigt dann eine mögliche Indexfragmentierung massiv an.
Es gibt Sachen wie SequentialId und ähnliche Konzepte, aber im Allgemeinen liegt man mit einem 64Bit Integer immer noch am besten, wenn man nicht exakt weiß was man macht.
Je nach was du mit Bitrate/SamplingRate usw machen willst in AudioStream wäre Decimal besser, double eigentlich nicht für sowas gedacht oder halt gleich integer...

Was soll denn IsMain sein?
 
  • Gefällt mir
Reaktionen: BeBur, madmax2010 und KitKat::new()
Danke für die schnelle Antwort!

@xdave78
Mein Gedankengang hinter den MetaDataType-Tabellen:
Darin lege ich z.B. einen Eintrag an, der über den Regex im Movie-Ordner die *.tbn Datei als Hauptcover ermittelt. Wird also ein Movie-Ordner gescannt, gehe ich die Einträge in der MovieMetaDataType Tabelle durch und suche mir über den Regex die entsprechende Datei. Damit erstelle ich dann einen Eintrag in der MovieMetaData-Tabelle, die eben auf den gescannten Movie und auch auf die den MetaDataType zeigt, damit ich dann im nachhinein über den Type-Enum weiß, wie ich die Datei anzuzeigen/zu parsen habe.

@Tornhoof
Die Bitrate und SampingRate (bzw. alle Properties in VideoStream und AudioStream) bekomme ich so von MediaInfo ausgelesen, daher habe ich den Datentyp einfach so mitübernommen (verwende dafür MP-MediaInfo).

Der MetaData-Eintrag, der mit IsMain gekennzeichnet ist, soll später in einer Grid-View direkt angezeigt werden. Die anderen MetaData-Einträge dann erst, wenn ein Item aus dem Grid für die Detailanzeige ausgewählt wird.

Danke für den Tipp mit der ID, haben bisher in der Arbeit auch immer Guid verwendet, daher hatte ich das so übernommen.
 
DrCox1911 schrieb:
Der MetaData-Eintrag, der mit IsMain gekennzeichnet ist, soll später in einer Grid-View direkt angezeigt werden. Die anderen MetaData-Einträge dann erst, wenn ein Item aus dem Grid für die Detailanzeige ausgewählt wird.
Wenn du Purist bist, ist deine Motivation dafür falsch, das sollte eine eigene Tabelle dann sein, das DB Schema sollte im Allgemeinen unabhängig von der Anzeige sein. Je nach wie puristisch der Ansatz sein soll, geht das natürlich oder macht ggf. einen View der das mit abbildet.
 
Hm, bin mir nicht sicher, ob ich dich richtig verstehe. D.h. streng genommen sollte so etwas wie mein IsMain gar nicht in die DB. Wo sollte so etwas dann abgelegt sein? Oder meinst du so, wie der erste Teil deiner Antwort vermuten lässt, das IsMain einfach noch auslagern in eine eigene Tabelle und über die ID dann zum entsprechenden MetaDataType linken?
 
Streng genommen ist IsMain nur ein Anzeige Artifakt.
Wenn die Information ob IsMain True ist in der Datenbank in einer anderen Tabelle liegt, dann sollte die Kombination maximal in einem View passieren (join).

Wenn es ein berechneter Wert ist, also z.b. alle Metadaten die im Namen Main beinhalten -> View via computed property z.b

Wenn es ein Wert ist den deine App berechnet, dann soltlest du eine eigene Tabelle dafür haben. MainMetadata oder so und dann ref auf die andere Tabelle.

Das ist primär eine purismus frage, du hast aber eh schon Themen wie Displaynamen in deinen Tabellen, damit kannst du auch IsMain drin lassen...
Ergänzung ()

DrCox1911 schrieb:
Die Bitrate und SampingRate (bzw. alle Properties in VideoStream und AudioStream) bekomme ich so von MediaInfo ausgelesen, daher habe ich den Datentyp einfach so mitübernommen (verwende dafür MP-MediaInfo).
Wenn du dir
https://github.com/yartat/MP-MediaI...diaInfo.Wrapper/MediaInfoWrapper.cs#L558-L560
anschaust, castet der Entwickler selber auf integer rauf. Das ist primär legacy code anscheinend.
Also würde ich da auch int nutzen.

Mit double wirst du nicht froh, insb. nicht in datenbanken.
 
Zuletzt bearbeitet:
  • Gefällt mir
Reaktionen: BeBur
Angenommen man nutzt keine neue Tabelle:
Statt den bool isMain evtl. einfach die ID des "Main" in MovieMetaData abspeichern?
 
Auf den ersten Blick würde ich auch sagen das ist viel zu kompliziert. Insbesondere jede 1-zu-1 Relation die nur in einer Tabelle benutzt wird muss ihre Existenz rechtfertigen, es ist viel einfach das in eine Tabelle zusammenzufassen. Das betrifft die ganzen Meta Tabellen, ich sehe da einfach nicht denn Sinn.

Ich würde alles als NOT NULL setzen bei dem es keinen Grund gibt das diese Spalte nicht gefüllt sein sollte. Insbesondere die Boolean Flags, da macht es selten Sinn True, False und Null zu haben, nur True und False ist einfacher. Nur wenn man unterscheiden muss zwischen "Die Information ist nicht da" und "False" macht es Sinn die Spalte als Nullable zu lassen.

Was mir noch auffällt ist das sehr viel von den direkt Dateibezogenen Spalten wie RegexString in mehreren Tabellen dupliziert sind. Eventuell wäre es da besser Serien, Filme, Staffeln und Episoden als high-level Konzepte zu haben, und dann die eigentlichen Videodateien generisch zu verwalten als eine eigene Entity. Wenn ich auf Dateiebene arbeite ist mir generell egal ob das jetzt ein Film oder eine Episode einer Serie ist.
 
Tornhoof schrieb:
Wenn es ein Wert ist den deine App berechnet, dann soltlest du eine eigene Tabelle dafür haben. MainMetadata oder so und dann ref auf die andere Tabelle.

Das ist primär eine purismus frage, du hast aber eh schon Themen wie Displaynamen in deinen Tabellen, damit kannst du auch IsMain drin lassen...
Danke für deine Antwort, das mit double ändere ich dann. Speziell auf das Zitierte:
Ich glaube, meine Intention hinter den zwei MetaDataType-Tabellen ist immer noch nicht ganz klar von mir rübergebracht worden.

Die zwei Tabellen werden vom User (natürlich über eine Form in der Web-UI) gefüllt. D.h. der User kann damit angeben, welche MetaDaten er mit eingelesen haben will.
Kodi legt im Ordner für einen Film z.B. folgendes ab (Movie steht dabei einfach für den Filmnamen):
  1. Movie.nfo: Darin speichert Kodi alles mögliche, z.B. IMDB-Bewertung, Plot, Schauspieler, ...
  2. Movie.tbn: Cover des Film, wird in der Cover-Ansicht verwendet
  3. backdrop.jpg: Hintergrundbild mit Filmmotiv
  4. banner.jpg: Filmmotiv für Anzeige in der Banner-Ansicht statt der Cover-Ansicht
Der User legt jetzt z.B. folgende Einträge in der MovieMetaDataType Tabelle:
  1. Movie.nfo
    • Type = nfo (=> damit weiß mein Programm dann, dass es diese Datei als nfo parsen kann)
    • Regex = (.nfo)$ (=> damit hole ich mir aus all den Dateien die raus, die diesem Muster entsprechen)
    • IsMain = false (da nicht der nfo-Inhalt in der Grid-View angezeigt werden soll)
  2. Movie.tbn
    • Type = picture
    • Regex = (.tbn)$ (=> damit hole ich mir aus all den Dateien die raus, die diesem Muster entsprechen)
    • IsMain = true (da dieses Bild in der Grid-View angezeigt werden soll)

Das ist jetzt nur mal ein kurzes Beispeil.
D.h. die Inhalte in den MetaDataType-Tabellen kommen vom User und werden als Konfiguration in der DB gespeichert.

Die eigentlichen Daten für einen Film liegen dann in der MovieMetaData-Tabelle und verweisen für die gefunden Dateien anhand der Einträge in MovieMetaDataType eben darauf.

Deinen letzen Abschnitt aus dem Zitat oben verstehe ich jedoch nicht. Meinst du mit "Displaynamen" die "Name"-Spalten? Darin speichere ich den eigentlichen Namen ab, bei Filme halt den Namen des Film und bei Serien eben den Seriennamen, Staffelnamen (meist Season 01... Season XX) und den Episodennamen.
Den könnte man zugegebenermaßen auch immer wieder über den unter FilePath angegeben Pfad von der Platte lesen, nur will ich damit auch arbeiten können, wenn die entsprechenden Platten im Spindown sind. Daher die Ablage in der DB.

Dalek schrieb:
Ich würde alles als NOT NULL setzen bei dem es keinen Grund gibt das diese Spalte nicht gefüllt sein sollte. Insbesondere die Boolean Flags, da macht es selten Sinn True, False und Null zu haben, nur True und False ist einfacher. Nur wenn man unterscheiden muss zwischen "Die Information ist nicht da" und "False" macht es Sinn die Spalte als Nullable zu lassen.
Das ist tatsächlich nur meiner Sichtweise geschuldet. Soetwas wie bool ist in C# ja standardmäßig nicht nullable und daher nicht explizit von mir so gekennzeichnet. Aber ja, das war der Plan, so wenig Null-Werte wie nötig zulassen.
@Dalek Könntest du deinen Beitrag nochmal etwas genauer ausführen?
Die doppelten MetaData und MetaDataType Tabellen sind da, da es bei Serien und Filmen halt komplett unterschiedliche MetaDaten gibt. Ob ich jetzt zwei getrennte Tabellen für einmal Serien und einmal Filme habe, oder aber beides in einer Tabelle mit entsprechender Kennzeichnung, ob dieser Eintrag jetzt für Filme oder für Serien zählt, kommt doch aufs gleiche hinaus, oder nicht?
 
DrCox1911 schrieb:
Die doppelten MetaData und MetaDataType Tabellen sind da, da es bei Serien und Filmen halt komplett unterschiedliche MetaDaten gibt. Ob ich jetzt zwei getrennte Tabellen für einmal Serien und einmal Filme habe, oder aber beides in einer Tabelle mit entsprechender Kennzeichnung, ob dieser Eintrag jetzt für Filme oder für Serien zählt, kommt doch aufs gleiche hinaus, oder nicht?
Ich verstehe nicht ganz wie die funktionieren sollen, ich hab nur gesehen das die die gleichen Spalten haben. Aber der Teil der eigentlich die Metadaten speichert ist nicht im Diagram.

Für flexible Metadaten die nicht in ein starres Schema passen würde ich JSONB Columns in Postgres verwenden. In EF Core kann man die dann entweder als unstrukturiertes "object" benutzen und z.B. einfach so an den Client ausgeben, oder man kann auch ganz normale EF Core Entities definieren die eben in JSONB gespeichert werden und nicht in einer eigenen Tabelle.

Wenn ich flexible Metadaten speichern müsste würde ich das als jeweils eine JSONB Spalte in den Tabellen Movies, TVShow, Season und Series machen.
 
Zurück
Oben