Rolf B: SQL-Datenbank für Rennergebnisse erstellen

Beitrag lesen

Hallo Christian,

der erste Schritt ist die Datenmodellierung in einem Entity-Relationship Modell. Eine Entity (deutsch: Entität) ist ein Objekt in deinem System, die sich typischerweise durch einen Schlüssel und weitere Attribute auszeichnet. Ein Relationship ist eine Beziehung zwischen Entitäten. In der Modellierung werden Beziehungen immer zwischen zwei Entitäten dargestellt. Wenn es sich herausstellt, dass eine Beziehung zwischen mehr als 2 Entitäten benötigt wird, setzt man eine Verknüpfungsentität dazwischen.

Bei Beziehungen ist die sogenannte Kardinalität wichtig. Das ist die Anzahl der Entitäten, die auf den Seiten einer Beziehung stehen können. Beispielsweise besteht zwischen einem Rennen und dem Lauf eines Rennens eine 1:N Beziehung. Zu einem Rennen gehören mehrere Läufe.

Die Beziehung zwischen Fahrer und Lauf ist dagegen eine M:N Beziehung. Ein Fahrer kann an beliebig vielen Läufen teilgenommen haben oder noch teilnehmen. An einem bestimmten Lauf nehmen mehrere Fahrer teil.

Vermutlich hast Du auch eine M:N Beziehung zwischen Fahrer und Rennen, die die Anmeldung eines Fahrers für ein bestimmtes Rennen darstellt und aus der die Teilnahme an allen Läufen dieses Rennens folgt. Das weiß ich aber nicht, das ist spezifisch für deine Problemstellung. Meldet man sich für einen Lauf an? Oder für ein Rennen, und startet dann in allen Läufen?

Es gibt auch so etwas wie 1:1 Beziehungen, die sind aber meist technischer Natur (wenn z.B. eine Entität eine Gruppe von Attributen hat, von denen entweder alle oder keins gebraucht werden, kann man diese Attribute in eine Extratabelle auslagern und damit Speicher sparen. Diese Attributgruppe könnte auch andere Zugriffsberechtigungen erfordern und deshalb in eine eigene Tabelle kommen).

Bei Dir sehe ich die Entitäten Fahrer, Rennen und Lauf. Dazu kommt auf jeden Fall eine M:N Beziehung Fahrer_Lauf (für die Zeit in einem Lauf). Ggf. gibt es eine M:N Beziehung Fahrer_Rennen (habe ich oben erklärt). Und ganz sicher gibt es eine 1:N Beziehung Rennen_Lauf.

Eine 1:N Beziehung realisiert man so, dass man in der Entität der N-Seite die ID der 1-Seite speichert. In einem Lauf speicherst Du also die ID des Rennens, zu dem er gehört. Man nennt das einen Fremdschlüssel. In manchen Fällen kann ein Fremdschlüssel auch aus mehreren Feldern zusammengesetzt sein.

Eine M:N Beziehung kann man in einer relationalen Datenbank nur mit Hilfe einer Zwischentabelle realisieren, in der die Fremdschlüssel von beiden Seiten gespeichert werden. Das schadet aber nichts, weil es ja für jede Paarung Fahrer und Lauf auch eigene Attribute gibt.

Die Frage, wo Attribute wie "Datum", "Rundenzahl", "Zeit", "Position" und "Punkte" hingehören, muss man nun Attribut für Attribut untersuchen. Wenn ich ein Attribut auf der N-Seite einer 1:N Beziehung anordne, muss ich mich immer fragen: haben alle Datensätze der N-Seite, die den gleichen Wert für den Fremdschlüssel aufweisen, den gleichen Wert? Ist es beispielsweise in einem Rennen so, dass alle Läufe die gleiche Rundenzahl aufweisen? Wenn ja, gehört die Rundenzahl zum Rennen, nicht zum Lauf.

Eine andere Frage ist, ob man bestimmte Attribute speichern muss. Beispielsweise brauchst Du die Position nicht zu speichern, sondern kannst sie durch Sortieren der Fahrer eines Laufs nach ihrer Zeit gewinnen, und damit auch die Punkte. Diese Informationen zu speichern ist aus Sicht des fachlichen Datenmodells also unnötig (redundant).

Es ist aber so, dass Redundanzen die Schmiere der Datenbank sind. Zum einen macht man sich daran die Finger schmutzig (weil man mehr Arbeit hat, um sie aktuell zu halten), man kann darauf ausrutschen (wenn man dabei was falsch macht), aber die Datenbank flutscht auch besser (Zugriffe können schneller werden). Jede Redundanz muss wohlüberlegt sein. In deinem Fall würde ich Position und Punktezahl nicht ständig neu berechnen, sondern an der Beziehung Fahrer_Lauf speichern.

Es scheint also sinnvoll, für dein Datenmodell 4 bis 5 Tabellen zu verwenden.

  • Fahrer (ID, Name, eMail, ...)
  • Rennen (ID, Name, Anzahl Runden, ...)
  • Lauf (ID, RennenID, Datum, ...)
  • Fahrer_Rennen(FahrerID, RennenID, ...)
  • Fahrer_Lauf(FahrerID, LaufID, RennenID, Zeit, Position, Punkte)

Die RennenID ist in der Fahrer_Lauf Tabelle eigentlich unnötig, weil man sie über die Beziehung Rennen_Lauf rückgewinnen kann. Das ist wieder eine Redundanz, deren Nutzen man abwägen muss.

Einen View pro Rennen würde ich NICHT erzeugen. Du musst unterscheiden zwischen der Modellierung einer DB und der Pflege ihrer Inhalte. Ein View gehört zum Modell, das Anlegen eines Rennens ist Inhalt. Wenn Du bei Pflege der Inhalte etwas am Modell ändern musst, ist das falsch. Wenn Du für eine Abfrage Tabellen- oder Spaltennamen dynamisch generieren musst, ist das meistens auch falsch. Eine Ausnahme für den letzten Satz ist deine Anzeige des Tabellenstandes. Dies ist eine Pivot-Tabelle, wo Zeilen und Spalten basierend auf DB-Inhalten aufgebaut werden. So etwas kann man direkt im SQL lösen (Pivotabfrage, die von MYSQL ab Version 8 unterstützt wird), es ist aber meistens einfacher, das programmatisch zu lösen (also im PHP Code).

Einen View kannst Du beispielsweise für die Teilnahme eines Fahrers an Läufen erzeugen:

CREATE VIEW laufteilnahme
  SELECT f.ID as FahrerID, l.ID as LaufID, l.RennenID
         f.Name, l.Datum, l.Zeit, l.Position, l.Punkte
  FROM fahrer f JOIN fahrer_lauf fl ON f.ID=fl.FahrerID
                JOIN lauf l ON l.ID=fl.LaufID

Was du dann nutzen könntest für alle absolvierten Läufe der Teilnehmer des Rennens mit ID 4711

SELECT FahrerID, LaufID, Name, Datum, Zeit, Position, Punkte
FROM laufteilnahme
WHERE RennenID=?

Für das ? setzt Du die gewünschte Renn-ID ein. Entweder gleich beim Erzeugen des Query-Strings (dann den Kontextwechsel beachten), oder durch gebundene Parameter in mysqli oder PDO.

Hoffe, dass Du damit einen Schritt weiterkommst.

Rolf

--
sumpsi - posui - clusi