Announcement

Collapse
No announcement yet.

RefreshSQL

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    Hallo,

    Erstmal vielen Dank für die präzise Erläuterung. Allerdings verschiebt es die Problemstellung nur hinter das post.

    Aus den Erläuterungen ist m.E. zu folgern:

    1)
    Jede Anwendung, die Datensätze erzeugt muß <b>höchstpersönlich</b> einen eindeutigen Wert generieren, um erzeugte Sätze auch wiederfinden und benutzen zu können. ( also genau das nochmal zu erledigen, was eigentlich der serverseitige PK sicherstellen soll )

    2)
    Den ganzen Klimbim mit zusätzlichen UNIQUE-Kriterien kann ich mir sparen, wenn ich mir den serverseitig über Generatoraufruf in der INSERTSQL-Anweisung generierten PK in meiner Anwendung als 'Wartemarke' abhole und nach dem INSERT anwendungsseitig eintrage.

    Daraus folgen wiederum folgende Fragen:

    a)
    Wo bleibt dann das FAT-Server Prinzip ?

    b)
    Arbeitet RefreshSQL auf der gesamten Datenmenge, oder nur auf der letzten Version meiner laufenden Transaktion ?

    c)
    Ist bei b) letzteres der Fall, was ist zu einem RefreshSQL-Konstrukt a' la ... WHERE ID=MAX(ID) zu sagen

    Comment


    • #17
      Hallo,

      wenn bestimmte Regeln im Datenmodell (Tabelle) eingehalten werden, ist ForceRefresh in der Lage, den vom Trigger gesetzten Primärschlüsselwert automatisch zu ermitteln. Allerdings stellt sich die Frage, ob das <b>vorherige</b> Abholen (so wie es mit den BDE-Komponenten schon immer üblich war) des neuen Generator-Wertes in einigen Fällen nicht die bessere Alternative ist.

      zu Frage a) <br>
      IBX ist prinzipiell in der Lage, den Wert automatisch zu ermitteln, wenn bestimmte Regeln eingehalten werden. Somit liegt hier kein Widerspruch vor.

      zu Frage b) <br>
      Jede SQL-Datenbank arbeitet immer nur im Kontext der laufenen Transaktion. Gemäss den ACID-Regeln einer Transaktion darf die eigene Transaktion keine Daten von fremden Transaktionen sehen, die zum Zeitpunkt des Starts der eigenen Transaktion noch nicht über Commit bestätigt wurden (READ COMMITTED ist eine offiziell tolerierte Ausnahme von dieser Regel, aber nicht der Normalfall).

      zu Frage c) <br>
      Das Problem mit MAX(ID) habe ich bereits in einer vorherigen Antwort angesprochen. In einer Mehrbenutzerumgebung arbeitet MAX erst dann eindeutig, wenn die Tabelle über TABLE STABILITY FOR PROTECTED WRITE so abgeschottet wird, dass faktisch eine Einbenutzer-Umgebung wirksam ist (d.h. während der eigenen Transaktionsdauer hat <b>niemand</b> Schreibrechte an dieser Tabelle). Da dies wohl nur sehr selten in Frage kommt, ist auch MAX(ID) nur in den seltensten Fällen eine Lösung für dieses Problem

      Comment


      • #18
        Hallo,

        nochmal zu b/c) nachgehakt,

        Auch wenn alle Transaktionen mit dem Isolationlevel RepeatableRead arbeiten sollte mir MAX() doch immer den letzten geposteten Satz meiner Transaktion liefern. Denn:

        1)
        Sofern keine Transaktion Manipulationen am Generator vornimmt, liefert dieser immer einen eindeutigen, fortlaufender Wert.

        2)
        Jeder in meiner Transaktion erzeugte und gepostete Satz muß danach zwangsläufig den höchsten Generatorwert besitzen.

        3)
        Erzeugte Sätze (mit evtl. höheren ID-Werten ) anderer Transaktionen bekomme ich erst nach Ende der eigenen zu Gesicht.

        zu a)
        Allerdings wird das Einhalten dieser Regeln immer schwieriger, je höher die Daten normalisiert sind.

        Oder betrachte ich die Sache zu blond

        Comment


        • #19
          Hallo Gesine,

          mit MAX(ID) bekommst Du spätestens dann Probleme, wenn der Generator "überläuft", d.h. negative Werte annimmt. Bei einem "Überlauf" eines Generators wird keine Exception ausgelöst!

          Tschüß

          Torste

          Comment


          • #20
            Hallo,

            ich gebe zu, dass die Sache mit MAX einfach zu verlockend ist... ;-)

            Warum ich es trotzdem nicht verwende, hat vor allem 2 Gründe: <br>
            1. Der InterBase ist aufgrund der Multigenerationen-Architektur im Gegensatz zu anderen SQL-Datenbank nicht gerade fix beim Heraussuchen des aktuellen MAX-Wertes (d.h. es darf nicht einfach nur der höchste Index-Wert ermittelt werden, sondern er muss zusätzlich die OIT+OAT berücksichtigen). <br>
            2. Das Ganze funktioniert nur dann, wenn garantiert REPEATABLE READ (aber nicht READ COMMITTED) verwendet wird und die eigene Transaktion erst nach dem Ermitteln des MAX-Wertes über Commit/CommitRetaining beendet wird (hier sorgt der Zeitbedarf für MAX für eine Verlängerung der aktiven Transaktionszeit). Über die BDE-Einstellung DRIVER FLAGS kann die im Programm vorgenommene Konfiguration überschrieben werden, so dass ein gewisses Restrisiko beim Zugriff über die BDE bleibt (beim Zugriff über IBX ist das aber kein Thema mehr).

            P.S: Ab dem InterBase 6 verwendet der GENERATOR 64-Bit-Integer. Die Gefahr eines Überlaufs sollte sich damit nach hinten verschieben, solange die Tabellenspalte für diesen Wert entsprechend gross deklariert wird

            Comment


            • #21
              Eigentlich ist es doch wohl egal ob MAX(ID) nur die eigene Transaktion berücksichtigt oder nicht, denn beides ist schlecht: Wird nur die eigene Transaktion berücksichtigt und in Verschiedenen Transaktionen werden sätze eingefügt, kommt nur der durch, der seine Transaktion als erster beendet, alle anderen bekommen eine Exception, weil sie eben die gleiche MAX(ID) haben. ist MAX(ID) global, erwischt man eben evtl. beim Refresh den falschen Datensatz, also immer Pech gahabt. Warum nicht in AfterInsert über einen zweiten Query (bei mir gibts immer eine Query namens Shorty für so was ) die genid abholen und selbst einsetzen, das sind drei zeilen. dann sollte man lediglich (zur sicherheit) noch den Primary index (also id) zusätzlich auf NOT NULL setzen, falls man irgendwo was übersehen hat. (wenn ich mich richtig erinnere lässt IB sonst einmal NULL zu?!) Naja und für die drei Zeilen im Programm kann (bzw. muß natürlich) sich IB ja das einsetzen sparen ;

              Comment


              • #22
                Hallo,

                ich bin davon ausgegangen, das Gesine über MAX(ID) den letzten vom eigenen Programm eingefügten Datensatz ermitteln wollte, aber nicht über MAX(ID einen neuen Primärschlüsselwert vergeben wollte. Wenn ein Trigger den Primärschlüsselwert über einen Generator zuweist, kann der Client diesen Wert nur dann automatisch auslesen und die Anzeige aktualisieren, wenn der neu eingefügte Datensatz über eine WHERE-Bedingung logisch eindeutig gefunden werden kann. Gesine hatte vor, als logisch eindeutiges WHERE-Kriterium für RefreshSQL den MAX(ID)-Wert der eigenen Transaktion (Repeatable Read alias Snapshot) zu nutzen. In diesem Fall wäre überhaupt kein weiterer Aufruf notwendig, da IBX dies automatisch im Hintergrund erledigt

                Comment


                • #23
                  Hallo,

                  zu Andreas:

                  Genau das war Vater des Gedankens.

                  zu Karsten:

                  Ich will "integritätsbedrohende" Funktionen soweit wie möglich vom Datenbankserver erledigen lassen.

                  a)
                  Wenn nun eine Anwendung beim Einfügen eines Satzes schon für die Vergabe eines nicht-sprechenden PK's sorgen muß ( egal ob mit 3 oder 30 Zeilen ), hat der Server als höchste Kontrollinstanz doch schon verloren.

                  b)
                  Solange alle Tansaktionen mit Snapshot arbeiten, schließt das Vorgehen Exceptions in jedem Fall aus ( mir mag auf die Schnelle auch kein trifftiger Grund einfallen, warum man einen anderen Isolationlevel nutzen sollte... ).

                  c)
                  Halten sich Entwickler nicht and den von mir vorgeschriebenen Isolationlevel für den Zugriff auf die Datenbank, dann erben deren Applikationen Exceptions, nicht meine.

                  d)
                  Allerdings könnten "Fummeleien" am Generator höcht eklig werden. Dazu habe ich einen neuen Thread in Interbase Server eröffnet

                  Comment


                  • #24
                    Hallo,

                    Uff, was für ein Krampf. Die Lösung mit MAX(ID) funktioniert tadellos. Leider funktioniert jetzt das Editieren nicht mehr, da die RefreshSQL-Angaben sich von der ForcedRefresh Einstellung absolut unbeeindruckt zeigt und nun auch beim Editieren immer munter den letzten Datensatz vorkramt. :-(

                    Ich habe das Poblem jetzt folgendermaßen gelöst:

                    Jede Tabelle erhält eine zusätzliche Spalte COUNTER.<p>
                    In den AfterEdit und AfterInsert-Routinen wird dieses Feld immer mit einem Anwendungsseitig generierten fortlaufenden Wert gefüllt.<p>
                    Meine RefreshSQL-Anweisung lautet entsprechend (...WHERE COUNTER=:COUNTER )<p>;
                    In der BeforeTransactionEnds-Routine, werden alle COUNTER-Felder wieder auf 0 gesetzt

                    Comment


                    • #25
                      Stimmt, das ist natürlich dann ein running gag, an den ich vorher auch noch nicht gedacht hatte Wie eine Bank sowas wohl bei der Konteneinrichtung löst?-

                      Comment


                      • #26
                        Hallo Gesine,

                        in diesem Fall würde ich doch wieder auf das klassische Modell zurückwechseln, und den Generatorwert vorher über eine Stored Procedure etc. abholen (so wie man das die ganze Zeit bei den BDE-Komponenten auch gemacht hat) und bei BeforePost der Spalte zuweisen

                        Comment


                        • #27
                          Hallo Andreas,

                          In einem Anfall von geistiger Umnachtung habe ich im laufenden Projekt von IBX 4.2 auf Version 4.3 'upgedated'. Nun verlangt IBX plötzlich sogar einen clientseitig gesetzten Wert im PK-Feld. Netter Spaß, hat nur 6h gedauert das Problem zu isolieren und beseitigen.

                          Die Counter-Methode erzeugt zwar geringfügig mehr Serverlast, hat aber den Charme die Generatoren zumindestens virtuell vollständig in der DB zu kapseln

                          Comment

                          Working...
                          X