Andreas: Status(gelesen?) von messages an mehrere user speichern

Hallo!
Hätte da nochmal ein konzeptionelles Problem:
Ich hab das jetzt wunderbar(naja, für netscape noch nicht wirklich gut) mit private messanges geschafft, und zwar speichere ich die immer in der DB wie folgt:

Tabelle: "pm"

+----+-----+----+---------+---------+--------+------+
| ID | von | an | subject | message | status | time |
+----+-----+----+---------+---------+--------+------+
| 1  |...  |... |...      |...      |...     |...   |
+----+-----+----+---------+---------+--------+------+
| 2  |...  |... |...      |...      |...     |...   |
+----+-----+----+---------+---------+--------+------+

So, das klappt prima bei den messages, die ich von einem User zu einem anderen schicke, im status schreibe ich standardmäßig "neu", und prüfe dann bei jedem user immer, ob es neue Datensätze für Ihn gibt. Nach dem lesen setze ich status = "gelesen".
So weit so gut. Nur würde ich auch gerne vom Admin aus messages an ALLE user senden, aber wenn ich sowas einfach in der Tabelle ablege, sieht zwar der erste, dass er eine neue Nachricht hat, aber sobald der sie liest wird sie als "gelesen" markiert und alle anderen haben bemerken das nicht mehr. Auch wenn ich das anders abfrage, dass die Nachricht z.B. nicht als "gelesen" markiert wird, habe ich auf der anderen Seite das Problem, dass die Nachricht bei jedem für immer als "neu" stehen bleibt. Das kann man auch mit der einen Tabelle nicht umgehen.
Hat das Sinn für jeden neuen User immer eine eigene Tabelle zu erstellen? Wird dann nur irgendwann sehr unübersichtlich befürchte ich! Ich wieß dass man das so pauschal nicht unbedingtr sagen kann, da Ihr ja auch den Rest nicht so kennt, kann man denn hier allgemein zu einer Vorgehensweise raten? Also die Usrdaten sind auch nicht sehr umfangreich, ca. 15 Spalten. Auch das Datenvolumen wird in absehbarer Zeit sehr überschaubar bleiben, ausgehend von 1.000-10.000 Auktionen pro Jahr, mit im Schnitt 10 Bietern.
Nur die Komunikation muß funktionieren!

Was würdet Ihe empfehlen?
Grüsse
  Andreas

  1. Hi Andreas,

    Tabelle: "pm"
    +----+-----+----+---------+---------+--------+------+
    | ID | von | an | subject | message | status | time |
    +----+-----+----+---------+---------+--------+------+
    | 1  |...  |... |...      |...      |...     |...   |
    +----+-----+----+---------+---------+--------+------+
    | 2  |...  |... |...      |...      |...     |...   |
    +----+-----+----+---------+---------+--------+------+

    Aha. _Eine_ Tabelle.
    Das ist ja nun nicht wirklich das, was man unter einer Datenbank
    versteht, aber für eine stikte 1:1-Relation könnte es noch taugen.

    So, das klappt prima bei den messages, die ich von einem User zu
    einem anderen schicke

    Sehnsewohl. ;-)

    So weit so gut. Nur würde ich auch gerne vom Admin aus messages
    an ALLE user senden

    Na also - da ist sie ja, die 1:n-Beziehung, die sich mit der einen
    Tabelle nicht mehr ausdrücken läßt. Fein ... :-)

    aber wenn ich sowas einfach in der Tabelle ablege, sieht zwar der
    erste, dass er eine neue Nachricht hat, aber sobald der sie liest
    wird sie als "gelesen" markiert

    Das darf sie nicht. Denn dieses Feld "gehört" ja nicht diesem Leser,
    sondern eigentlich allen.
    Genau das ist das Problem: Das verkraftet Dein Datenmodell nicht.

    Auch wenn ich das anders abfrage, dass die Nachricht z.B. nicht
    als "gelesen" markiert wird, habe ich auf der anderen Seite das
    Problem, dass die Nachricht bei jedem für immer als "neu" stehen
    bleibt. Das kann man auch mit der einen Tabelle nicht umgehen.

    Richtig. Deine Tabelle kann für _eine_ Nachricht bisher nur einen von
    zwei Statuswerten darstellen: "gelesen" und "nicht gelesen".
    So schwarz-weiß ist Deine Welt aber inzwischen nicht mehr - Du brauchst
    jetzt "Grautöne".

    Hat das Sinn für jeden neuen User immer eine eigene Tabelle zu
    erstellen?

    Um Himmelswillen, nein!

    Ich wieß dass man das so pauschal nicht unbedingtr sagen kann,

    Doch, das kann man in diesem Fall ganz bestimmt.
    Es darf nicht notwendig sein, Dein Programm anzupassen, nur weil ein
    neuer Benutzer dazu kommt.

    kann man denn hier allgemein zu einer Vorgehensweise raten?

    Ja.

    Also die Usrdaten sind auch nicht sehr umfangreich, ca. 15 Spalten.
    Auch das Datenvolumen wird in absehbarer Zeit sehr überschaubar
    bleiben, ausgehend von 1.000-10.000 Auktionen pro Jahr, mit im
    Schnitt 10 Bietern.

    Das wäre noch nicht mal entscheidend. Die richtige Vorgehensweise ist
    in diesem Falle nicht wirklich vom Datenvolumen abhängig.
    Ein einziger Artikel, auf den dann doch tausend Leute bieten wollen,
    darf Dir nicht weh tun - damit muß Dein Modell locker fertig werden.

    Was würdet Ihe empfehlen?

    Eine saubere relationale Zerlegung.

    Du verwendest doch eine relationale Datenbank, weil Du Relationen, also
    Beziehungen darstellen willst.
    Dazu mußt Du allerdings die Objekte herausarbeiten, zwischen denen die
    Beziehungen existieren - und die Art der Beziehung herausfinden.

    Einerseits hast Du eine Beziehung zwischen den Nachrichten und ihren
    Attributen. Das ist die naheliegende 1:1-Beziehung, die Du bereits
    modellierst hast: Jede Nachricht hat jedes Attribut einmal.
    Das "Objekt" ist in diesem Falle die Nachricht, repräsentiert durch
    ihre ID; die übrigen Spalten Deiner Tabelle sind dessen Eigenschaften.

    Dieses Objekt hat Deiner Meinung nach noch ein weiteres Attribut, nämlich
    seinen "Lesestatus".
    Dieser ist nun aber gerade _keine_ 1:1-Beziehung mehr, sondern eine 1:n-
    Beziehung zwischen einem Sender und n Lesern! Die Nachricht ist hier gar
    nicht mehr das Objekt selbst.
    Zur Repräsentation brauchst Du also ein n-Tupel aus lauter einzelnen
    Schaltern, die auf "gelesen" oder "ungelesen" stehen können.
    Deine Tabelle hat aber nur Platz für einen Schalter.
    Und pro Nachricht sind es auch noch unterschiedlich viele - eine Tabelle
    mit beliebig, aber konstant vielen Spalten löst Dein Problem nicht (es
    verschlimmert die Lage nur, auch wenn ich schon öfters solche "Lösungen"
    als reale Implementierung gesehen habe - weia), genauso wie eine Tabelle
    pro Benutzer es nicht löst.

    Bisher hast Du nur Zeilen in Deiner Tabelle, welche durch _eine_ Spalte
    eindeutig beschrieben werden. Diese Eigenschaft nennt man "Primärschlüssel
    der Tabelle". Technisch gesehen kann jede Tabelle nur einen einzigen sol-
    chen Primärschlüssel besitzen; inhaltlich kann es durchaus vorkommen, daß
    sich mehrere Spalten dafür eignen würden.
    Deine zweite Tabelle wird einen Primärschlüssel haben, der aus _zwei_
    Spalten bestehen wird, nämlich der _Kombination_ aus ID und Leser. Das
    erlaubt Dir, 1:n-Beziehungen darzustellen. (Und sogar m:n-Beziehungen.)
    Genau damit kannst Du in dieser neuen Tabelle sämtliche Lesezustände
    darstellen (und sonst nichts!) - der Schalter ist dann die dritte Spalte
    dieser Tabelle. Nun hat jeder Leser einer Nachricht seine eigene Zeile
    für diese Nachricht in dieser Tabelle - keiner überschreibt mehr etwas
    Fremdes. Die einzelnen Benutzer-Tabellen, die Du anlegen wolltest, haben
    wir nun in eine einzige Tabelle "gefaltet".

    Sicherlich willst Du irgendwann den "allgemeinen Lesezustand" einer Nach-
    richt herausfinden - eine typische Fragestellung wäre "gibt es noch jeman-
    den, der die Nachricht nicht gelesen hat?".
    Dazu wirst Du die Nachricht über die erste Tabelle adressieren - vielleicht
    mußt Du ihre ID ja erst finden - und dann über diese ID als gemeinsamem
    Spaltenwert aus der zweiten Tabelle alle zugehörigen Lesezustände holen
    und prüfen.
    Diese Verknüpfung zwischen den beiden Tabellen nennt man in SQL einen
    "JOIN", und das wird für den Rest Deines SQL-Lebens Dein ständiger Be-
    gleiter werden.
    Überlege Dir mal, wie Du diese Aufgabe mit vielen einzelnen Benutzer-
    Tabellen hättest lösen wollen! Du hättest zuerst mal den Namen der Tabelle
    berechnen müssen, um die SQL-Abfrage auf diese Tabelle erzeugen zu können.
    Mit einer einzigen großen Tabelle sieht diese SQL-Abfrage immer gleich aus

    • der Benutzername taucht einfach in der WHERE-Klausel auf, um die Lese-
      schaltermenge dieses Benutzers aus der Gesamtmenge herauszufiltern.

    In der Datenbanktheorie gibt es übrigens Verfahren, wie man eine solche
    Zerlegung zwingend herleiten oder doch mindestens den entsprechenden
    Bedarf zuverlässig feststellen kann - das nennt sich dann "Normalformen".
    Ein solcher Entwurf ist nämlich bei zwei Tabellen noch problemlos durch
    Hinsehen möglich - aber stell Dir mal vor, Du hast hundert Tabellen, oder
    tausend, oder ...

    Noch etwas solltest Du aus diesem Fall mitnehmen: Bei relationalen Daten-
    banken ist eine korrekte Datenmodellierung unbezahlbar - die SQL-Abfragen
    schreiben sich anschließend beinahe von selbst.
    Ein mißlungener Entwurf dagegen ist oftmals nicht nur umständlicher zu
    behandeln, sondern fehleranfälliger, inperformanter (hier kann es leicht
    um mehrere Zehnerpotenzen gehen) und mit viel mehr Pflegeaufwand der
    eigenen Software verbunden. Einen Fehler in dieser Phase zu übersehen
    kann den gesamten Software-Entwurf zum Scheitern verurteilen - das ist
    in SQL viel schlimmer als bei Drittgenerations-Sprachen.

    Viele Grüße
          Michael

    1. Hallo!

      Vielen, vielen Dank für Deine ausführliche Antwort! Hat mir sehr geholfen und werde versuchen dass so gut wie möglich auf die gesamte DB anzuwenden!
      Also werde ich in meine Db eine neue Tabelle einfügen mit drei Spalten, message_ID, leser_ID und status.
      Wenn ich das so mache kann ich ja auch hervorragend vorher auswählen, z.B. an ausgewählte Leser schicken, und dann werden halt hier die entsprechenden Einträge gemacht, auch die Auswertung auf der anderen Seite, wirklich super!!!
      Und im Prinzip ist das ja schon eine m*n Beziehung, da es ja nicht nur mehrere Leser gibt, sondern auch mehrere Messages, nur wird meist eine Message an mehrer User geschickt.
      Nur eine kurze Frage, muß ich in mysql dann die beiden Spalten als Primärschlüssel deklarieren? Was ich noch nicht ganz verstehe, was mir der Primärschlüssel _praktisch_ bringt, für die Theorie und die Entwicklung der Datenbank sicher sinnvoll, aber in der Praxis, z.B. bei JOINS suche ich mir doch eh die Spalten aus, über die die Tabellen "verbunden" werden sollen, jedenfalls soweit ich das weiß! Und bei einzelnen Tabellen-Abfragen ist das eh irrelevant.
      Warum macht man das denn dann in der Praxis?

      Vielen Dank nochmal!

      mfg
        Andreas

  2. Hallo Andreas,

    Ich hab das jetzt wunderbar(naja, für netscape noch nicht wirklich gut) mit private messanges geschafft

    Netscape spielt hier nun wirklich keine Rolle, da es sich nicht um browserspezifische Anforderungen handelt.

    Der ausfuehrlichen Darstellung von Michael ist nichts mehr hinzuzufuegen. Er hat alles wie immer sehr treffend und genau beschrieben.

    Tabelle: "pm"
    +----+-----+----+---------+---------+--------+------+
    | ID | von | an | subject | message | status | time |
    +----+-----+----+---------+---------+--------+------+
    | 1  |...  |... |...      |...      |...     |...   |
    +----+-----+----+---------+---------+--------+------+
    | 2  |...  |... |...      |...      |...     |...   |
    +----+-----+----+---------+---------+--------+------+

    Als Nachtrag vielleicht nur ein Trivialbeispiel fuer Deine Nachrichten.

    Tabelle 1:

    Message-Nummer
    Subject
    Body
    Sendedatum
    Anzahl User
    Absender
    usw. (spezielle Kategorisierung)

    Tabelle 2:

    Message-Nummer
    Empfaemger
    Status
    Lesedatum

    Der Bezug wird immer ueber die Message-Nummer hergestellt, weitere Verknuepfungen koenntest Du ueber die sonstigen Kategorisierungen herstellen.

    Gruesse
    Wilhelm

    1. Hallo!

      Netscape spielt hier nun wirklich keine Rolle, da es sich nicht um browserspezifische Anforderungen handelt.

      Ja, das selbst nicht, war auf einen alten Thread von mir bezogen, da wo ich nach Lösungen für ein möglichst aktuelles messaging-Fenster gesucht habe, im IE kein Problem, aber im NN hebe ich nur unbefriedeigende Lösungen gefunden, da sich jedesmal der Frame kpl. neu aufbaut, was IMHO sehr nervig ist, im IE bekommt man das nicht mit!

      Tabelle 1:

      Message-Nummer
      Subject
      Body
      Sendedatum
      Anzahl User
      Absender
      usw. (spezielle Kategorisierung)

      Wozu denn Anzahl der User? Könnte ich doch ggfs auch aus Tabelle 2 abfragen?!

      Tabelle 2:

      Message-Nummer
      Empfaemger
      Status
      Lesedatum

      Was würdet Ihr mir raten, wie ich ein Datum am besten in mySQL(mit php) abspeichere? Also ich kenne da genug Varianten, aber irgendwie finde ich das alles nicht richtig optimal. Zum einen der Timestamp, den kann ich ohne Probleme in php auslesen, nur wird der bei jeder Änderung geändert, so dass das z.B. für Absendedatum nicht in Frage kommt. Jetzt gibt es ja die Varienten DATE, DATETIME, und TIME. Gibt es hier eine Möglichkeit, z.B. automatisch in einem der Felder die aktuelle Zeit als Voreinstellung einzustellen, wenn ein neuer Datensatz angelegt wird? Soweit ich weiß kann man in PHP einfach now() bei inserts scheiben, mich interessiert nur, ob man sowas wie einen Timestamp nützen kann, der sich aber hinterher nicht mehr verändert, ohne das man das mit einträgt.
      Oder wie ist das überhaupt mit den formatierten Zeit-Feldern, was bringen die denn für Vorteile, als wenn man einfach ein varchar Feld nimmt, und die Zeit so aus php einträgt?
      Und die letzte Frage, was nehme ich da am besten(wie formatiert), wenn ich den Zeitpunkt in der Tabelle in php zur Berechnung einer Zeitdifferenz oder ähnliches verwenden will, z.B. mit date()?

      Grüsse
        Andreas