Modellierung des “Let’s Meet”-Datenbankprojekts (LS Beispiellösung Teil 1)

https://oer-informatik.de/lets_meet-beispielloesung_modellierung

https://bildung.social/@oerinformatik/110207682728246503

tl/dr; Dies ist der erste Teil (Modellierung) einer Beispiellösung für die “Let’s Meet” Datenbank-Lernsituation. Es führen viele Wege zum Ziel - der einzig richtige ist der Weg, den man selbstständig gegangen ist. Daher ist diese Variante bestenfalls als eine Ideenskizze zu verstehen, die genutzt werden kann, wenn man nicht mehr weiterkommt. Einige Schritte habe ich bewusst etwas umständlicher realisiert, um so bestimmte Techniken des Datenbankentwurfs bzw. Abfragetechniken darstellen zu können. (Zuletzt geändert am 23.05.2023)

Dieser Artikel ist Teil einer mehrteiligen Serie, in der

Allen Artikel enden mit einem SQL-Skript, mit dem Daten als Ausgangspunkt für die nächste Etappe genutzt werden können, falls es zu Problemen bei der Eigenentwicklung kam.

Ausgangslage

Die eigentliche Aufgabestellung der “Let’s Meet” Datenbank-Lernsituation kann hier nachgelesen werden. Ein Datenbankdump liegt als Excel-Datei (hier) vor ist wirklich von unerträglicher Qualität - offensichtlich entspringt er einer Exportabfrage, die zwar alle Daten enthält, jedoch keine sinnvolle Struktur aufweist. Zudem sind die Inhalte redundant gespeichert. Wir müssen hier wohl auf vielen Ebenen Probleme lösen (siehe Beispielauszug):

Nachname, Vorname Straße Nr, PLZ Ort Telefon Hobby1 %Prio1%; Hobby2 %Prio2%; Hobby3 %Prio3%; Hobby4 %Prio4%; Hobby5 %Prio5%; E-Mail Geschlecht (m/w/nonbinary) Interessiert an Geburtsdatum
Forster, Martin Minslebener Str. 0, 46286, Dorsten 02372 8020 Fremdsprachenkenntnisse erweitern %78%; Im Wasser waten %80%; Schwierige Probleme klären %61%; Morgens Früh aufstehen %17%; martin.forster@web.ork m w 07.03.1959
Elina, Tsanaklidou Gartenweg 13, 69126, Heidelberg 06221 / 98689 Jemanden massieren %57%; Mir die Probleme von anderen anhören %21%; Abends seinem Partner Ereignisse des Tages erzählen %53%; Für einen guten Zweck spenden %25%; tsanaklidou.elina@1mal1.te nb m 28.02.1958

Problemanalyse

Offensichtlich gibt es eine Reihe von Schritten, die erforderlich sind:

  • Es muss eine sinnvolle Zieldatenbank entworfen werden,

  • die vorliegenden Daten müssen in die Struktur der Zieldatenbank gebracht werden

  • und die Daten müssen in diese Zieldatenbank importiert werden.

Während der Entwurf des Zielsystems noch recht einfach zu realisieren ist, gibt es für den Import der Daten in diese Struktur sehr viele Varianten, die sich in zwei grundlegende Strategien zusammenfassen lassen:

  • Einlesen und Anpassen der Daten per Script. Die Logik, die die Daten aufteilt, findet im Script statt.

  • Importieren der Ursprungsdaten in eine Tabelle und Normalisierung / Umformung der Daten per SQL-Befehl.

Für beide Varianten gibt es Vor- und Nachteile. Die Aufgabenstellung sieht jedoch vor, dass die Ausgangs-Datenstruktur zunächst importiert werden sollen und daraufhin mit weiteren SQL-Statements in die Zielstruktur überführt werden sollen.

Planung Teil 1: Datenbankentwurf des Zielsystems

Planung Teil 2: Die Ursprungsdaten importieren (Details in einem zweiten Schritt, später)

Planung Teil 3: Die Ursprungsdaten in das Zielsystem überführen (Details in einem dritten Schritt, später)

Konzeptuelles Datenmodell: Welche Daten befinden sich in der Excel-Datei

Aus der Ursprungstabelle lassen sich direkt zwei Entitätstypen ableiten: Hobby und User. Eine Reihe von Attributen geben die Spaltenüberschriften unmittelbar her, davon sind einige mehrwertig (Name, Address):

Entity-Relationship-Diagramm der Let’s Meet DB mit den Entitätstypen User und Hobby und mehrwertigen Attributen
Entity-Relationship-Diagramm der Let’s Meet DB mit den Entitätstypen User und Hobby und mehrwertigen Attributen

Die Beziehung zwischen User und Hobby hat selbst ein Attribut (Priorität), es handelt sich bei der Relationship also um einen assoziativen Entitätstypen (Rechteck mit Raute). Da dieser Entitätstyp ohne User und ohne Hobby nicht existiert, habe ich ihn als schwachen Entitätstypen notiert (doppeltes Rechteck).

Außerdem müssen die mehrwertigen Attribute noch aufgelöst werden. Gender wird zudem ausgelagert, da es auf zwei verschiedene Arten berücksichtigt werden muss: Das Geschlecht, als das ein User gelesen werden will, sowie das Geschlecht der anderen User, an dem ein User Interesse hat. Mit den nötigen Kardinalitäten versehen sieht das Ganze etwa so aus:

Entity-Relationship-Diagramm der Let’s Meet DB mit den Entitätstypen User, Hobby, Gender
Entity-Relationship-Diagramm der Let’s Meet DB mit den Entitätstypen User, Hobby, Gender

Bleiben noch die Erweiterungen, die im Datenmodell berücksichtigt werden sollen:

  • das Profilbild wird als neuer Entitätstyp Picture mit einer 1:1 Relationship zu Users notiert

  • die Fotos werden über denselben Entitätstyp modelliert, jedoch mit einer 1:n Relationship

  • Freundesbeziehungen werden als m:n Relationship umgesetzt

  • Hobbies der anderen und deren Priorität analog zu den eigenen Hobbies mit einem schwachen assoziativen Relationshiptypen.

Die Erweiterung des Modells sieht somit so aus:

Entity-Relationship-Diagramm der Let’s Meet DB mit den zusätzlichen Entitätstypen Pictures und “Meets people with” und der Relationship “Friendship”
Entity-Relationship-Diagramm der Let’s Meet DB mit den zusätzlichen Entitätstypen Pictures und “Meets people with” und der Relationship “Friendship”

Um die einzelnen Kardinalitäten und Optionalitäten festzulegen, werden die einzelnen Beziehungen mit Hilfe von ERDish formuliert. ERDish formuliert je Relationship zwei Sätze nach dem Muster:

EACH Entity A MUST | MAY Relationship ONE AND ONLY ONE | ONE OR MORE Entity B.

Im Einzelnen ergeben sich also für jede Relationship zwischen zwei Entitätstypen zwei Sätze:

User - Hobby

EACH User MAY like ONE OR MORE Hobby.

EACH Hobby MAY be liked by ONE OR MORE User.

EACH User MAY like people having ONE OR MORE Hobby.

EACH Hobby MAY of persons may liked by ONE OR MORE User.

User - Gender

EACH User MAY be seen as ONE AND ONLY ONE Gender.

EACH Gender A MAY be the gender of ONE OR MORE User.

Ich gehe also bei diesem Modell davon aus, dass die Geschlechtszuordnung eindeutig ist und sich in Schubladen einteilen lässt (z.B. maskulin / feminin / non-binary, aber diese Liste ist offen für Erweiterungen).

Zeitgemäßer ist es, anstelle des Geschlechts einfach die Pronomen oder die Anrede zu erfassen. Da für viele auf einer Dating-Plattform das Geschlecht eine Rolle spielt, habe ich das zunächst so umgesetzt. Ich bin nicht ganz zufrieden damit und bin für Hinweise dankbar.

Die zweite Beziehung wird etwa so umgesetzt:

EACH User MAY be interested in ONE OR MORE Gender.

EACH Gender A MAY be the interest of ONE OR MORE User.

User-Picture

EACH User MAY have as a profile picture ONE AND ONLY ONE Picture.

EACH Picture MAY be the profile picture of ONE AND ONLY ONE User.

EACH User MAY own ONE OR MORE Picture.

EACH Picture MUST be owned by ONE AND ONLY ONE User.

User-User

EACH User MAY be a friend of ONE OR MORE User.

EACH User MAY be a friend of ONE OR MORE User.

Aus den ERDish-Sätzen ergeben sich v.a. Optionalitäten, die im ER-Diagramm so nicht deutlich waren. Ein angepasstes ER-Diagramm mit Optionalität und ergänzt um die Ausgangsdaten (Ursprung) sieht etwa so aus:

ER-Diagramm mit Ursprungstabelle und eingetragenen Optionalitäten
ER-Diagramm mit Ursprungstabelle und eingetragenen Optionalitäten

Logisches Datenmodell: Welche Daten befinden sich in der Excel-Datei

Zur Umwandlung in das logische Modell muss zunächst das Datenbankmodell ausgewählt werden. In unserem Fall soll es das Relationenmodell sein, daher müssen die Transformationsregeln des Relationenmodells angewendet werden:

  • Aus allen Entitätstypen werden Relationen gebildet.

  • In jeder Relation wird geprüft, ob es natürliche Schlüsselattribute (oder Attributgruppen) gibt, die einen Datensatz eindeutig beschreiben. Wo dies nicht der Fall ist werden künstliche Primärschlüssel (artificial primary keys) vergeben. Diese werden durch den Suffix _id gekennzeichnet.

  • Die Relationen werden entgegen der Konvention nicht kleingeschrieben und im Plural beschrieben, sondern nach ungarischer Notation mit vorangestelltem tbl (für table) und englischen Bezeichnungen benannt.

  • 1:n bzw. 1:nc Beziehungen werden über Schlüsselattribute auf der “n”-Seite aufgelöst. Wenn keine Optionalität vorliegt (also bei 1:n) wird zudem ein NOT NULL-Constraint notiert.

  • m:n wird über eine Zwischentabelle realisiert. Im Fall des assoziativen Relationshiptypen mit einem Attribut in der Zwischentabelle. Mit Optionalität wird analog zu oben mit NOT NULL verfahren.

Relationenmodell auf Basis des ERD mit Constraints und Verknüpfungsrelationen
Relationenmodell auf Basis des ERD mit Constraints und Verknüpfungsrelationen

Erstellung des Physisches Modells: Die SQL-DDL-Befehle

Auswahl der Datentypen

Durch die Modellierung wurden bereits eine Reihe von Entscheidungen getroffen, die jetzt nur in SQL-DDL-Code umgesetzt werden müssen. Noch geklärt werden müssen die Fragen hinsichtlich der Datentypen und Constraints in der Datenbank:

Zeichenketten:

  • Müssen die Daten performanceoptimiert sein (CHAR) oder speicherplatzoptimiert (VARCHAR)? Speicherplatz ist schon länger kein Problem mehr, ich wähle daher CHAR.

  • Bis zu welcher Länge sollen Zeichenketten gespeichert werden? _

  • Welche Zeichencodierung soll genutzt werden? Hier bietet sich UTF8 an (wir haben internationale Namensschreibweisen). UTF8 sollte ohnehin bei den meisten DBMS der Standard sein.

Indizes, Schlüssel, Constraints:

  • Sollen diese als fortlaufende Nummer oder UUID realisiert werden? UUID sind zwar der Weg der Wahl, in vielen DBMS aber ab Werk noch nicht möglich. Daher zunächst laufende Nr.

  • Sollen die Constraints und Trigger direkt mit umgesetzt werden?

Datumswerte:

  • Welcher Datentyp soll gewählt werden?

  • Welche Lokalisierung soll gewählt werden?

Constraints:

  • Welche Zusicherungen müssen für einzelne Felder oder für Kombinationen aus Feldern gegeben werden? Wird im Einzelnen je Tabelle entschieden.

  • In welchen Fällen ist beim Löschen eine Löschweitergabe sinnvoll und wo nicht? Wird ein User oder ein Hobby gelöscht, so sollen alle verweisenden Daten gelöscht werden. Wird ein Geschlecht gelöscht, so sollen alle Daten erhalten bleiben.

Startpunkt ist eine Datenbank. In einigen Datenbankmanangementsystemen muss diese zunächst erzeugt werden, dann muss eine neue Verbindung zu der Datenbank aufgenommen werden, da alle connections jeweils datenbankabhängig sind.

Vorbereitung der Datenbank

Die Datenbankverbindung und Vorbereitung des Frontends wird im zweiten Teil dieser Beispiellösung dargestellt. Hier soll es zunächst nur um die reine DDL-Struktur gehen. Zum Nachstellen der Befehle kann es trotzdem sinnvoll sein, bereits jetzt ein gestartetes DBMS zu haben.

(bei PostgreSQL z.B. auf diesem Weg).

Damit das Projekt in einer eigenen Datenbankstruktur hinterlegt ist, muss diese zunächst erzeugt werden:

Bei PostgreSQL verbindet sich ein Frontend bei einer Datenbankverbindung immer direkt mit einer Datenbank - hier müsste daher eine neue Verbindung zur neuen DB aufgebaut werden, um fortzufahren.

Die Projektdaten sollten in einen eigenen Namensraum gelegt werden, damit wir ggf. nicht mit anderen Tabellen in Konflikt geraten. Namensräume werden in den meisten DBMS als Schema organisiert:

Erstellen der Tabellen

Die Tabellen müssen in der richtigen Reihenfolge erstellt und gelöscht werden: Alle Tabellen, die keinerlei Fremdschlüssel enthalten können unabhängig erstellt werden (tblGender, tblUrsprung, tblHobby). tblUser muss nach tblGender erstellt werden, tblProfilePicture zuletzt, alle übrigen nach tblUser. Alternativ können die Tabellen zunächst erstellt werden und später die entsprechenden Foreign Key Constraints per ALTER TABLE nachgereicht werden.

Abhängigkeit zwischen den Tabellen als Gantt-Diagramm
Abhängigkeit zwischen den Tabellen als Gantt-Diagramm

Wenn mal etwas schiefgeht, möchte ich die Tabellen schnell löschen und habe die entsprechenden Befehle immer direkt mit verzeichnet. Aber beim Löschen gilt genau das Gegenteil: Ich kann tblGender, tblUser, tblHobby und tblPicture erst löschen, nachdem es keine Fremdschlüsselbeziehungen mehr gibt. Die Lösch-Befehle muss ich also in umgekehrter Reihenfolge ausführen.

Die Ursprungstabelle ist noch am einfachsten: es wird zunächst alles als Text eingelesen - ich wähle den performanceoptimierten CHAR als Datentyp, die Länge wähle ich zunächst per Bauchgefühl und muss ggf. später noch anpassen. Mit der UserID wird ein Schlüssel mit laufender Nummer erzeugt.

In postgres wird für fortlaufende IDs die Kurzform SERIAL verwendet:

tblGender umfasst lediglich die Werte m, w, nb, da ich noch nicht 100% überzeugt bin, dass das dem Anwendungsfall gerecht wird, gönne ich mir hier 8 Zeichen.

tblUser nutzt im Wesentlichen die getroffenen Datentypenentscheidungen von Ursprung - die Größen der vormals zusammengefassten Attribute wurden halbiert. Birthday wird als reines Datumsfeld gespeichert. Mit gender ist es erstmals nötig, einen Fremdschlüssel zu verzeichnen (Constraint), entsprechend hat das Attribut den gleichen Datentyp wie oben.

Die Einträge in der Tabelle tblGenderInterest sind nur für bestehende Userkonten relevant. Daher wurde beim Foreign Key Constraint für die user_id eine Löschweitergabe vorgesehen (ON DELETE CASCADE). Selbiges wäre auch beim Löschen des Geschlechts denkbar, diese Information würde ich aber sicherheitshalber behalten. Der Primärschlüssel ist hier ein zusammengesetzter, der gesondert benannt werden muss:

Bei tblHobby2User und tblUserLikesPeopleWithHobby gelten dieselben Überlegungen: Löschweitergabe, zusammengesetzter Primärschlüssel:

Bleiben noch die Tabellen der erweiterten Aufgabenstellung: die Tabelle tblFriendship modelliert die selbstreferenzierende Beziehung, entsprechend verweisen zwei Foreign Keys auf user_id. Wie bei allen Userkontenbezügen wird auch hier eine Löschweitergabe vorgesehen:

Bei tblPicture ist die Besonderheit, dass mit einem Check-Constraint sichergestellt wird, dass entweder eine URL oder ein Bild als Datei vorhanden ist. Der Dateityp von pictureData ist zum Speichern von Binärdateien vorgesehen.

Bleibt schließlich noch die Tabelle tblProfilePicture. Damit diese eine 1:1 Beziehung zwischen tblUser und tblPicture abbildet, muss ein UNIQUE-Constraint gesetzt werden.

Testen der SQL-DDL-Befehle

Aus der Modellierung ergeben sich bereits eine Reihe von Testfällen, die erst mit laufendem Datenbanksystem ausgeführt werden können. Damit sie nicht in Vergessenheit geraten, sollten sie bereits notiert werden:

Testfall
Nr.
Beschreibender Name der Testklasse / des Testfalls Vor-
bedingungen
Eingabewerte Erwartetes
Resultat
gemäß
Spezifikation
Nach-
bedingungen
Tatsächliches
Resultat
bestanden
/ nicht bestanden
DDL-1 Datentypen: die gewählten Datenbreiten sind ausreichend Import der größten vorgegebenen Daten Fehlerfreie Speicherung der Werte -
DDL-2 Laufende Nummern werden als Index vergeben -
DDL-3 Check-Constraint in tblProfilePicture Weder URL noch Datei wird übergeben Speicherung nicht möglich -
DDL-4 Löschweitergabe bei tblUser -
DDL-5 Keine Löschweitergabe bei tblGender -
DDL-6 Speichern von Einträgen mit NOT NULL Constraint-Verstoß nicht möglich -

Fazit der Modellierung

Damit wäre die Struktur für die Ausgangstabelle (von Excel nach tblUrsprung) und das Zielsystem modelliert und per SQL-Befehl definiert. Ein fertiges SQL-Skript findet sich hier für MariaDB, hier für MS SQL Server und hier für postgres.

Beispiele für den Import (Teil 2) und die Normalisierung im Bestand werden in zwei weiteren Artikeln beschrieben.


Hinweis zur Nachnutzung als Open Educational Resource (OER)

Dieser Artikel und seine Texte, Bilder, Grafiken, Code und sonstiger Inhalt sind - sofern nicht anders angegeben - lizenziert unter CC BY 4.0. Nennung gemäß TULLU-Regel bitte wie folgt: Modellierung des "Let’s Meet"-Datenbankprojekts (LS Beispiellösung Teil 1)” von oer-informatik.de (H. Stein), Lizenz: CC BY 4.0. Der Artikel wurde unter https://oer-informatik.de/lets_meet-beispielloesung_modellierung veröffentlicht, die Quelltexte sind in weiterverarbeitbarer Form verfügbar im Repository unter https://gitlab.com/oer-informatik/db-sql/lets-meet-db. Stand: 23.05.2023.

[Kommentare zum Artikel lesen, schreiben] / [Artikel teilen] / [gitlab-Issue zum Artikel schreiben]

Kommentare gerne per Mastodon, Verbesserungsvorschläge per gitlab issue (siehe oben). Beitrag teilen: