Matze: Datensatz vor dem speichern auf änderung vergleichen!

Ich habe in einer Datenbank meine Datensätze, nun wird über eine Eingabemaske der Datensatz Nr.1 aufgerufen, dieser wird in einem Formular angezeigt. Dort hat man die Möglichkeit diesen zu ändern. Jetzt wird dieser mit update über den alten Datensatz geschrieben.

Jetzt die Frage, gibt es eine einfache Möglichkeit den Datensätze zu vergleichen, um ihn erst zu speichern wenn auch wirklich eine Änderung vorgenommen wurde?

Matze

  1. Jetzt die Frage, gibt es eine einfache Möglichkeit den Datensätze zu vergleichen, um ihn erst zu speichern wenn auch wirklich eine Änderung vorgenommen wurde?

    Du kannst z.B. per JS prüfen, ob der Datensatz geändert wurde. Du kannst auch nach dem Absenden des Formulares erstmal den alten Datensatz auslesen und mit den neuen Daten vergleichen.

    Wenn es dir dabei im einen Performancegewinn geht, geh ich davon aus, dass du diese mit allen möglichen Mitteln eher verschlechterst wenn du nicht einfach plump den Datensatz überschreibst.

    1. Ich habe eher an einen Performancegewinn gedacht, aber ich denke es ist einfacher den Datensatz einfach neu zu speichern.

      1. Hello,

        Ich habe eher an einen Performancegewinn gedacht, aber ich denke es ist einfacher den Datensatz einfach neu zu speichern.

        Welche "Performance" willst Du denn stattfinden lassen? Die "Show" kannst Du dir sparen. Darum kümmern sich die DBMS meistens schon sleber. Und die paar Bytes, die als Traffic vom Client zum Server mitgesendet werden beim POST, die tun heutzutage nicht mehr weh. Es sein denn, Du würdest megabytedicke Bilder/Daten zurückschicken.

        Da gibt es viel wichtigere Dinge, die Du beachten müsstest. Siehe übrige Antworten im Thread.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
      2. Ich habe eher an einen Performancegewinn gedacht, aber ich denke es ist einfacher den Datensatz einfach neu zu speichern.

        Hi Matze,

        es gibt da einen schlauen Spruch, der für mich immer gut funktioniert:

        Repariere nichts, was nicht kaputt ist.

        Wenn Du keine Performanceprobleme hast ist es auch meist müsig, entsprechend zu optimieren. Es sei denn, Du musst Zeit totschlagen ;o)

        Cheers
        Sascha

    2. Hello,

      Du kannst z.B. per JS prüfen, ob der Datensatz geändert wurde.

      Nein, das kann er nicht. Relevant ist doch nur die Änderung des Datensatzes in der Tabelle auf dem Server. Und ob der zwischenzeitlich geändert wurde, kann der Client nicht feststellen.

      Du meintest jetzt zwar sicher, ob am Client die angezeigten Daten geändert wurden, aber das ist irrelevant. Sollte der Versuch stattfinden, unveränderte Daten per Update zurückzuschreiben, verwerfen die meisten SQL-DBMS das sowieso. Es gibt dann keine "affected rows".

      Probier es einfach mal aus.

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
       ☻_
      /▌
      / \ Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
      1. Nein, das kann er nicht. Relevant ist doch nur die Änderung des Datensatzes in der Tabelle auf dem Server. Und ob der zwischenzeitlich geändert wurde, kann der Client nicht feststellen.

        Also kann ich nicht z.B. per Ajax beim Server anfragen ob der Datensatz noch der gleiche ist?

        Wenn relevant ist, dass der Datensatz von mehreren Stellen geändert werden kann, muss der OP das erwähnen. Solange aber das keine ausdrückliche Bedingung ist, steht für mich fest, dass eine Änderung nur von einem Rechner aus möglich ist.

        1. Hello,

          Nein, das kann er nicht. Relevant ist doch nur die Änderung des Datensatzes in der Tabelle auf dem Server. Und ob der zwischenzeitlich geändert wurde, kann der Client nicht feststellen.

          Also kann ich nicht z.B. per Ajax beim Server anfragen ob der Datensatz noch der gleiche ist?

          Es fragt sich, zu welchem Zweck du das erfragen willst. Wenn Du den Datensatz ändern willst, ist es egal, ob der Request per AJAX oder per normalem stateless Post übermittelt wird. Wichtig ist nur, ob der Datensatz geändert wurde, seitdem der Client ihn das letzte Mal gelesen hat. Und das geht nur auf dem Server _direkt__während__des__Änderungsversuches_. Dazu muss aber entweder das DBMS einen Conflict-Zähler bereitestellen (bTrieve tut das), oder aber ind Datenmodell und die Abfragen muss einer eingebaut werden.

          Wenn relevant ist, dass der Datensatz von mehreren Stellen geändert werden kann, muss der OP das erwähnen. Solange aber das keine ausdrückliche Bedingung ist, steht für mich fest, dass eine Änderung nur von einem Rechner aus möglich ist.

          Das ist sogar von einer einzigen "Stelle" aus schon relevant.

          Und an die Fehlermöglichkeit wird der OP garantiert genausowenig gedacht haben, wie Millionen von anderen Web-Applikations-Entwicklern. Ich halte es daher für wichtig, darauf hinzuweisen.

          Im braucht man also nur einen Counter einzubauen, diesen beim Holen der Daten mit auszulesen und z.B. als Hash mit an den Client zu liefern. Beim Posten der geänderten Daten sendet der Client diesen Hashwert mit zurück zum Server.

          Die Änderung findet dann mit einem

          Update tabelle
              set
                spalte = $spalte,
                ... ,
                counter = counter + 1
              where id = $id
                and hash(counter) = $hash;

          statt.

          Bitte aber Escaping und Quotierung der Variablen nicht vergessen, wo sie notwendig sind!

          Sollte der Counter zwischendurch bereits erhöht worden sein, findet kein Update statt.

          Oder man speichert id und counter gleich in der Session unter einem Ramdom-Wert ab, den man dann mitsendet.

          Die Meldung für den Client könnte im Fehlfalle dann lauten: "Die gewünschte Änderung konnte nicht durchgeführt werden. Dies kann daran liegen, dass der DS inzwischen anderweitig gelöscht oder geändert wurde."

          Und dann kann/muss der Satz neu ausgelesen werden.

          Ob man dem Client die Wunschdaten von eben in einem separaten Bereich (Fenster, DIV, Popup) nochmal vorlegt zum Vergleich und/oder Copy und Paste, das ist dem Programmplaner überlassen.

          Liebe Grüße aus dem schönen Oberharz

          Tom vom Berg

          --
           ☻_
          /▌
          / \ Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de
  2. Tach!

    Jetzt die Frage, gibt es eine einfache Möglichkeit den Datensätze zu vergleichen, um ihn erst zu speichern wenn auch wirklich eine Änderung vorgenommen wurde?

    Nein, keine den "rauen" Bedingungen des realen Lebens standhält. Den Datensatz außerhalb des DBMS zwischenzuspeichern birgt die Gefahr, dass ein anderer Prozess den im DBMS ändert und somit die Daten nicht mehr übereinstimmen. Den Datensatz vorher dem Speichern abzufragen ist mit dem Aufwand einer SELECT-Statements verbunden. Statt einem Datentransport in Richtung DBMS hast du zusätzlich noch einen in die andere Richtung. Zudem müsste die ganze Aktion in einer Transaction gekapselt werden, sonst gibt es wieder die Gefahr der zwischenzeitlichen Änderungen durch andere Prozesse.

    Was ist dein eigentliches Problem das du lösen willst? Suchst du begründet nach einer Optimierungsmöglichkeit oder denkst du dir nur, dass es performanter wäre?

    dedlfix.

    1. Hello,

      Den Datensatz vor dem Speichern abzufragen, ist mit dem Aufwand einer SELECT-Statements verbunden.

      Nein. Das Bedarf nur einer Where-Clause.

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
       ☻_
      /▌
      / \ Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
      1. Tach!

        Den Datensatz vor dem Speichern abzufragen, ist mit dem Aufwand einer SELECT-Statements verbunden.
        Nein. Das Bedarf nur einer Where-Clause.

        Stimmt. Mit einem WHERE auf mindestens eine Ungleichheit über alle Felder lässt sich das Update direkt steuern. Aber gewonnen hat man damit kaum etwas. Die unveränderten Daten werden nach wie vor zum DBMS übertragen und erst kurz vor dem Ziel erspart man sich eine Schreiboperation, die das DBMS möglicherweise sowieso von sich aus wegoptimiert hätte. Sowas bringt nur dann Punkte, wenn das DBMS nicht selbst optimiert und ein aufwendig zu aktualisierender Index im Anschluss der Änderung nachgezogen werden muss.

        Die Verhinderung des Update-Vorgangs ist an sich nur dann sinnvoll, wenn fachliche Gründe dafür sprechen. Das heißt, wenn Daten nur dann geändert werden dürfen, wenn kein anderer zwischenzeitlich Änderungen vorgenommen hat. Ob man dazu die Tabelle um einen Sequenzzähler erweitert oder alle Felder einzeln mit dem jeweiligen alten Wert vergleicht, kommt darauf an, ob man die Tabelle um das Sequenzfeld erweitern kann oder ob etwas dagegenspricht.

        dedlfix.

  3. Hello Matze,

    Ich habe in einer Datenbank meine Datensätze, nun wird über eine Eingabemaske der Datensatz Nr.1 aufgerufen, dieser wird in einem Formular angezeigt. Dort hat man die Möglichkeit diesen zu ändern. Jetzt wird dieser mit update über den alten Datensatz geschrieben.

    Jetzt die Frage, gibt es eine einfache Möglichkeit den Datensätze zu vergleichen, um ihn erst zu speichern wenn auch wirklich eine Änderung vorgenommen wurde?

    Das tun die Datenbankmanagementsysteme meistens schon von ganz alleine, was aber dann ggf. später zu einem False Positive führen kann, wenn Du die Auswertung der Operation nicht genau durchdenkst.

    Viel wesentlicher in einem Internetsystem ist meistens die Konkurrenzsituation zwischen den Usern.

    Üb das am allerbesten einfach mal an einer einfachen Textdatei, die in einer Textarea angezeigt wird und dort vom User veränsert und wieder abgespeichert werden kann. Stell Dir das Szenario vor, wenn z.B. drei User die Seite aufrufen (die Textbox "laden"), sich eine Weile den Text darin ansehen, jeder an einer anderen Stelle Veränderungen vornehmen und dann den gänderten Text zurückspeichern.

    Du benötigst für diesen Fall einen Lockingmechanismus. Im Web (verbindungslose Client-Server-Applikation) wird der passende immer wieder gerne total vergessen. Ich habe ihm daher vor Jahren schon einen Sptznamen gegeben "Academic Locking". Das soll bedeuten, dass ein optimistic-Locking-Verfahren benutzt wird, das auch mit "akademischer Verspätung" = "ct" noch funktioniert.

    Es wird also nicht wirklich der Datensatz gesperrt, sondern ein Semaphor des Datensatzes mit ausgelesen. Dieses muss vor dem (beim) Zurückspeichern noch übereinstimmen. Direkt im Speichervorgang wird es jedoch geändert, z.B. einfach ein Zähler heraufgezählt.

    Für die Sicherheit sinnvoll ist es, wenn dem Client das Semaphor nicht im Klartext übermittelt wird, sondern dieses entweder als Hash übermittelt wird, oder aber das Semaphor nur in der API (Session) gespeichert wird.

    Das Update-Statement muss demnach in ihrer where-clause auch die Übereinstimmung des Semaphors überprüfen. Stimmt dieses nicht mehr überein, sollte nach Möglichkeit eine Exception mit eineindeutiger Fehlernummer ausgelöst werden. DBMS, die das noch nicht können, können aber häfig schon stored Procedures. Leider können die auch noch nicht immer gezielt Exceptions auslösen...

    Aber wenn wir noch warten, wird MySQl das auch eines Tages können :-D

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. Daumen hoch, klasse Beitrag!

      Gruß
      der mit den zwei Daumen
      T-Rex

  4. Hi there,

    ich werde einfach mal die Frage beantworten und die Schulstunde den Kollegen überlassen :o)
    Wenn ich Daten aus der Datenbank hole, landen die bei mir gewöhnlich in einem Objekt (Zum Beispiel "Kunde", "Rechnung" etc).
    Diesem Objekt gebe ich eine Boolean-Eigenschaft IsPersisted mit, die beim befüllen aus der Datenbank auf "True" gesetzt wird.
    In den Settern der anderen Eigenschaften wird geschaut ob der übergebene Wert ungleich dem vorhandenen ist, und wenn ja die IsPersisted-Eigenschaft auf "False" gesetzt.

    Wenn es jetzt ans speichern geht weiß ich, ob eine Änderung (Wohlgemerkt, eine  Änderung der bereits geladenen Daten) durchgeführt wurde (IsPersisted=False) und kann mich entscheiden was ich tue.

    Hoffe es hilft weiter.

    Ciao
    Sascha

    1. Hello Sascha,

      ich werde einfach mal die Frage beantworten und die Schulstunde den Kollegen überlassen :o)
      Wenn ich Daten aus der Datenbank hole, landen die bei mir gewöhnlich in einem Objekt (Zum Beispiel "Kunde", "Rechnung" etc).
      Diesem Objekt gebe ich eine Boolean-Eigenschaft IsPersisted mit, die beim befüllen aus der Datenbank auf "True" gesetzt wird.
      In den Settern der anderen Eigenschaften wird geschaut ob der übergebene Wert ungleich dem vorhandenen ist, und wenn ja die IsPersisted-Eigenschaft auf "False" gesetzt.

      Wenn es jetzt ans speichern geht weiß ich, ob eine Änderung (Wohlgemerkt, eine  Änderung der bereits geladenen Daten) durchgeführt wurde (IsPersisted=False) und kann mich entscheiden was ich tue.

      Und jetzt kommt der eigentlich spannende Teil. Was tust Du anshließend beim "Persistieren" deiner Daten aus dem Objekt?

      Wenn DU jetzt das, was ich geschrieben habe zum Thema aufmerksam durchliest und in deine Objektdefinition einfließen lässt, dann änderst Du deine "Persisted"-Eigenschaft von Boolean auf einen numerischen Wert oder NULL. NULL würde bedeuten, dass in der DB noch keine Daten vorhanden waren und der Datensatz beim Speichern neu angelegt werden müsste, ein numerischer Wert käme aus der DB. Wenn ein numerischer Wert vorhanden ist, muss der beim Speichern noch übereinstimmen mit dem Wert in der DB und muss beim Speichern geändert werden. Bringe das deinem Objekt bei.

      Wenn Du diese kleinen Änderungen durchgeführt hast, ist auch dein System in Zukunft mehrbenutzerfähig.

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
       ☻_
      /▌
      / \ Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
      1. Hi Tom,

        ich glaube immernoch das Mehrbenutzerfähigkeit garnicht Bestandteil der ursprünglichen Frage war und vielleicht für den Kollegen auch gar kein Thema ist. Konzeption für mehrere konkurierende Benutzer ist nicht ganz so trivial wie Du schon erwähnt hast, und erfordert ja auch ein gewisses maß an definition des Programmverhaltens (Was passiert im Frontend, Wie ist der Flow etc.).
        Nicht für jedes kleine Projekt ist das notwendig.

        Die Unterscheidung Update oder Delete geht für mich meist aus der ID des Datensatzes hervor, den ich in der Regel mit ins Objekt packe. Ist sie vorhanden, handelt es sich dabei um ein Update, ansonsten wird der Datensatz neu angelegt. Auch hier natürlich nicht so trivial wenn es um mehrere Benutzer geht.

        Ich finde es übrigens gut, eventuelle Probleme (Was passiert bei mehreren Benutzern) bei der Antwort aufzuzeigen. Versteh mich also nicht falsch. Aber die eigentliche Antwort auf die Ursprüngliche Frage sollte dabei nicht aus den Augen verloren werden. Und das passiert, wenn ich mir das Forum so anschaue, leider viel zu oft.

        Cheers
        Sascha

        ich werde einfach mal die Frage beantworten und die Schulstunde den Kollegen überlassen :o)
        Wenn ich Daten aus der Datenbank hole, landen die bei mir gewöhnlich in einem Objekt (Zum Beispiel "Kunde", "Rechnung" etc).
        Diesem Objekt gebe ich eine Boolean-Eigenschaft IsPersisted mit, die beim befüllen aus der Datenbank auf "True" gesetzt wird.
        In den Settern der anderen Eigenschaften wird geschaut ob der übergebene Wert ungleich dem vorhandenen ist, und wenn ja die IsPersisted-Eigenschaft auf "False" gesetzt.

        Wenn es jetzt ans speichern geht weiß ich, ob eine Änderung (Wohlgemerkt, eine  Änderung der bereits geladenen Daten) durchgeführt wurde (IsPersisted=False) und kann mich entscheiden was ich tue.

        Und jetzt kommt der eigentlich spannende Teil. Was tust Du anshließend beim "Persistieren" deiner Daten aus dem Objekt?

        Wenn DU jetzt das, was ich geschrieben habe zum Thema aufmerksam durchliest und in deine Objektdefinition einfließen lässt, dann änderst Du deine "Persisted"-Eigenschaft von Boolean auf einen numerischen Wert oder NULL. NULL würde bedeuten, dass in der DB noch keine Daten vorhanden waren und der Datensatz beim Speichern neu angelegt werden müsste, ein numerischer Wert käme aus der DB. Wenn ein numerischer Wert vorhanden ist, muss der beim Speichern noch übereinstimmen mit dem Wert in der DB und muss beim Speichern geändert werden. Bringe das deinem Objekt bei.

        Wenn Du diese kleinen Änderungen durchgeführt hast, ist auch dein System in Zukunft mehrbenutzerfähig.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        1. Tach!

          ich glaube immernoch das Mehrbenutzerfähigkeit garnicht Bestandteil der ursprünglichen Frage war und vielleicht für den Kollegen auch gar kein Thema ist.

          Sicherheit ist oftmals auch nicht Bestandteil ursprünglicher Fragen und anscheinend auch bislang kein Thema manch eines Fragenden. Trotzdem sehe ich es als wichtig an, auch solche und andere für die eigentliche Frage nebensächlichen Mängel aufzuzeigen. Unsichere Systeme können auch zum Nachteil Unbeteiligter (also auch zu meinem) verwendet werden, wenn darüber Spam versendet oder DDoS-Angriffe durchgeführt werden.

          Konzeption für mehrere konkurierende Benutzer ist nicht ganz so trivial wie Du schon erwähnt hast, und erfordert ja auch ein gewisses maß an definition des Programmverhaltens (Was passiert im Frontend, Wie ist der Flow etc.).
          Nicht für jedes kleine Projekt ist das notwendig.

          Es geht aus der ursprünglichen Frage nicht hervor, ob das relevant ist oder nicht. Auch nicht, ob das vielleicht später mal relevant werden könnte. Es ist auch nicht zwingend notwendig, den Vorschlag diesbezüglich umzusetzen. Wenn Matze das nicht braucht, kann er das auch gern selbst sagen. Andererseits ist es in diesem Forum nicht verboten, abseits der Frage weiterzudiskutieren.

          Ich finde es übrigens gut, eventuelle Probleme (Was passiert bei mehreren Benutzern) bei der Antwort aufzuzeigen. Versteh mich also nicht falsch. Aber die eigentliche Antwort auf die Ursprüngliche Frage sollte dabei nicht aus den Augen verloren werden. Und das passiert, wenn ich mir das Forum so anschaue, leider viel zu oft.

          Es könnte auch daran liegen, dass viel zu oft mitunter gravierende Mängel abseits der eigentlichen Frage zu sehen sind. Oder es ist einfacher, erstmal die anderen Mängel anzusprechen, weil das eigentliche Problem nicht ausreichend beschrieben wurde (neben der Bitte um eine Präzisierung natürlich). Auch ich bitte dich, mich nicht falsch zu verstehen. Es ist richtig, wenn du Fehler in Antworten siehst, diese anzusprechen. Auch eingefahrene Gleise auszubessern und wenn notwendig eine Weiche einzubauen, ist in der Regel wünschenswert. Dies muss ebenso gestattet sein, wie das Aufzeigen einer besseren Lösung, auch wenn der OP nicht daran interessiert ist (was wir™ üblicherweise jedoch nicht von vorn herein wissen können).

          dedlfix.

          1. Hi dedlfix,

            Ich habe in einem anderen Forum einen ähnlichen Fall.
            Und auch dort waren die Leute mehr damit beschäftigt Ihr Knowhow zu presentieren und die Fragestellung als Unzureichend und "Blödsinnig" abzutun.

            Ich finde es allerdings wichtig zunächst einmal die Frage an sich zu achten und zu beantworten, wenn sie sich beantworten läßt. Wenn zum Beispiel jemand frage wie er die Textfarbe auf Rot setzt wäre es doch sinnvoll das zunächst zu erwähnen bevor man in Diskussionen über die Sinnhaftigkeit von rotem Text, die Verwendung von ausgelagerten Stylesheets und den Tod von Whitney Houston einsteigt.

            Natürlich ist es legitim auch aus dem eigenen Nähkästchen zu plaudern und auf eventuelle Fallstricke hinzuweisen. Die Ratio sollte aber nicht bei 99/1 liegen.

            Wenn ich mir das recht überlege wäre diese Diskussion eigentlich in einem eigenen Thread besser aufgehoben, anstatt Matzes Eintrag damit zuzumüllen. Asche auf mein Haupt, vielleicht mach ich das nochmal, wenn es mich zu sehr beißt.

            So long
            Sascha

            1. Tach!

              Wenn ich mir das recht überlege wäre diese Diskussion eigentlich in einem eigenen Thread besser aufgehoben, anstatt Matzes Eintrag damit zuzumüllen. Asche auf mein Haupt, vielleicht mach ich das nochmal, wenn es mich zu sehr beißt.

              Jein. Ich finde es besser, Kritik an Ort und Stelle zu äußern anstatt einen allgemeinen "Mecker-Thread" zu eröffnen, in dem dann noch per Rundumschlag pauschal das ganze Forum beanstandet wird (mal als Extrem formuliert). Solch eine Kritik kommt in der Regel nicht bei den beabsichtigten Adressaten an, weil sich bei einer nicht namentlichen Nennung oder direkten Ansprache keiner angesprochen fühlen muss. Stattdessen senkt das nur die allgemeine Stimmung, was sich auch auf die auswirkt, die sich redlich bemühen. So zumindest meine Erfahrung mit mehreren solcher Vorkommnisse in der Vergangenheit. Also lieber freundlich aber direkt vor Ort ansprechen, das hat mehr Bezug zum beanstandeten Verhalten und zu den konkreten Verursachern. Man kann ja den Titel und Themenbereich ändern, womit deutlich gekennzeichnet ist, dass es sich in dem Zweig um ein Thema abseits des Problems handelt.

              dedlfix.

        2. Hello,

          ich glaube immernoch das Mehrbenutzerfähigkeit garnicht Bestandteil der ursprünglichen Frage war

          Gar hat mit nichts gar nichts zu tun :-)

          Dass Mehrbenutzerfähigkeit verlangt wird, kann man in einem Internet-System erstmal zu 99,99% voraussetzen.

          Erst wenn etwas anderes behauptet, beschrieben und passende Maßnahmen bewiesen werden, entfällt diese Aufgabe vielleicht.

          Und wenn Du schon so weit bist, dass Du ein intelligentes Datenbankobjekt erstellt hast und bekommst nun von jemandem, der Dir gar kein Geld dafür abnimmt, einen Tipp, wie Du es auf einfache Wege noch erheblich verbessern kannst, dann verstehe ich nicht, warum Du dich dagegen wehrst :-)

          Liebe Grüße aus dem schönen Oberharz

          Tom vom Berg

          --
           ☻_
          /▌
          / \ Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de