Announcement

Collapse
No announcement yet.

Aktualisierungsproblem mit IBDataset

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

  • Aktualisierungsproblem mit IBDataset

    Hallo,<br><br>
    ich habe zwar schon einige Threads zu IBDataset gelesen, habe aber noch nicht die Lösung für mein spezielles Problem gefunden. Deshalb hier ein neuer Thread.<br><br>
    Ich habe in einem Stammdatenfenster am Rand permanent ein Grid, das per TDatasource und TIBDataset gefüllt wird. Ich möchte so schnell zwischen vorhandenen Datensätze wechseln.<br>
    <br>
    Bei meiner TIBDataset-Instanz ist nur die Eigenschaft SelectSQL und RefreshSQL mit "select rohstoffe_id, bezeichnung from rohstoffe" gefüllt. Alles andere mache ich "per Hand" mit TIBSQL. Wird nun ein neuer Datensatz eingefügt, mache ich ein Insert per TIBSQL über einer andere (!) TIBTransaction als das IBDataset hat. Nach dem Commit ist der Datensatz in der IBConsole zu sehen, aber im Grid mit IBDataset trotz Refresh bzw. Close/Open nicht! Beim Ändern und Löschen tritt selbiger Effekt auf.<br><br>
    In der Anwendung mache ich mit dem IBDataset folgendes:<br>
    - Prepare<br>
    - Open<br>
    - FetchAll<br>
    - First<br>
    - dann irgendwann: Insert<br>
    - Commit<br>
    - Close + Open<br>
    <br>
    <br>
    Nun meine Fragen:<br>
    1.) Ist hier die Kombination DBGrid + IBDataset überhaupt sinnvoll?<br>
    2.) Liegt es an den verschiedenen Transaktionen, dass der Datensatz trotz Refresh nicht erscheint?<br>
    3.) Wenn ja, wie kann ich manuell die übrigen Transaktionen refreshen?<br>
    <br>
    Vielen Dank für eure Tipps und ein schönes Wochenende,<br>
    Christian Storfinger

  • #2
    Hallo,

    zu Frage 1: Ja.

    zu Frage 2: Ja.

    zu Frage 3: Dafür sieht IBX die Eigenschaft <b>IdleTimer</b> sowie das TIBTransaction-Ereignis <b>OnIdleTimer</b> vor. Im Unterordner <i>RedSys</i> ist ein Beispielprojekt zu finden, dass diesen Weg nutzt, um in regelmässigen Zeitabständen die Daten "anderer Benutzer" im TDBGrid auftauchen zu lassen. Der folgende Auszug stammt aus diesem Beispiel:

    <pre>

    {-------------------------------------------------------------------}
    { IBTransactionMain IdleTimer }
    {-------------------------------------------------------------------}

    {
    Eine der Grundregeln in der C/S-Welt lautet, dass der Client niemals
    die Zeitdauer einer Transaktion bestimmen darf. Dabei muss der Entwickler
    gleich 2 Fälle betrachten:
    a) Transaktionsdauer bei INSERT/UPDATE/DELETE (X-Locks)
    b) Transaktionsdauer bei SELECT (S-Locks)
    Die Schreibzugriffe verursachen ein X-Lock (Exklusive Sperre), daher
    muss die Zeitdauer dieser Sperre (= Gültigkeitsdauer der Transaktion)
    minimiert werden.
    Die Lesezugriffe verursachen ein S-Lock (Gemeinsame Sperre), wobei dank
    der Multigenerationenarchitektur des InterBase dies nicht so gravierende
    Folgen wie ein lange wirksamer X-Lock hat. Allerdings sollte auch hier
    die Zeitdauer der Transaktion so kurz wie möglich sein. Was bei der BDE
    kein Thema war (dank AUTOCOMMIT und der Intelligenz von TTable/TQuery),
    kommt bei IBX mit dem Dringlichkeitsvermerk auf die Tagesordnung. Bei
    IBX muss sich der Entwicklungs um alles selbst (!) kümmern, d.h. er
    hantiert direkt mit den InterBase-Transaktionen. Und da sogar ein einfacher
    Lesezugriff (SELECT) nur im Kontext einer Transaktion ausgeführt werden
    kann, gibt es ein Problem. Um dieses zu lösen, gibt es mehrere Möglichkeiten:
    a) Verzicht auf TDBGrid, Daten werden nur gelesen, in TEdits kopiert und
    danach die Transaktion über COMMIT beendet. IBX schliesst darauf alle
    aktiven Datenmengen (was hier nicht zum Problem wird).
    b) Verzicht auf TDBGrid kommt nicht in Frage, daher wird die Datenmenge
    sofort in eine In-Memory-Table (TClientDataSet oder ähnliches)
    kopiert und danach die Transaktion über COMMIT beendet. IBX schliesst
    darauf alle aktiven Datenmengen (was hier nicht zum Problem wird).
    Allerdings steigt der Aufwand, wenn die Daten auch editierbar sein
    sollen (Briefcase-Modell).
    c) Verzicht auf TDBGrid kommt nicht in Frage, Mehraufwand für In-Memory-
    Tabellen kommt nicht in Frage. In regelmässigen Abständen wird
    CommitRetaining aufgerufen - wobei IBX die Datenmenge offen hält und
    sich die aktuellen Datensatzposition nicht ändern.
    d) Verzicht auf TDBGrid kommt nicht in Frage, Mehraufwand für In-Memory-
    Tabellen kommt nicht in Frage. In regelmässigen Abständen wird
    Commit aufgerufen - wobei IBX die Datenmenge jedoch schließt. Wenn
    nach dem erneuten Öffnen der Datenmenge die "alte" Position sichtbar
    sein soll, muss dieser Datensatz vorher über ein eindeutiges Merkmal
    gespeichert werden.
    Das Programm soll sich generell den vom Bearbeiter zuletzte aufgerufenen
    Beitrag merken und diesen Datensatz beim nächsten Start sofort wieder
    anzeigen. Es liegt auf der Hand, diese Funktion auch für die Lösung des
    Transaktionsproblems zu nutzen:
    1. TIBTransaction-Eigenschaft IdleTimer: Immer dann auf 10000 Millisekunden
    setzen, wenn die Datenmenge IBDSBeitrag im State dsBrowse ist,
    anderenfalls den IdleTime über die Zuweisung von 0 deaktivieren
    2. TIBTransaction-Eigenschaft DefaultAction: TACommit -> IBX schließt dabei
    automatisch alle zu diesem Zeitpunkt offenen Datenmengen, die dieser
    TIBTransaction-Instanz zugewiesen waren.
    3. TIBTransaction-Ereignis OnIdleTimer auswerten: Datenmengen neu öffnen und
    alten Datensatz über die Primärschlüsselwerte suchen
    }

    const
    cIBDSBEITRAG_IdleTimer = 10000;

    //{$DEFINE DebugIdleTimer} // Compilerdirektive ist auskommentiert

    procedure TDM.IBTransactionMainIdleTimer(Sender: TObject);
    begin
    {$IFDEF DebugIdleTimer}
    MessageBeep(0);
    ShowMessage(Format('P: %d; A: %d, B: %D',
    [FCurrentPublikationID, FCurrentAusgabeID, FCurrentBeitragID]));
    {$ENDIF}
    DoOpenBeitrag;
    end;

    {
    Das Fenster für die Bearbeitung der Beiträge zu einer Ausgabe einer
    Publikation wird geöffnet. Das Programm muss genau den Datensatz
    wieder aktivieren, der vom Bearbeiten beim letzten Aufruf ausgewählt
    wurde. Dazu verwendet diese Methode schnellere und effektivere
    Alternativen zur Locate-Methode (die jedoch ebenfalls genutzt werden
    könnte).
    Nach dem Einlesen aller Datenmengen wird gleich ein CommitRetaining
    abgesetzt, um die aktuellen S-Locks zu entfernen.
    }

    procedure TDM.DoOpenBeitrag;
    begin
    FLocateMode := True;
    if not IBTransactionMain.InTransaction then
    IBTransactionMain.StartTransaction;
    // Alle Datensätze der Redakteur-Tabelle im Voraus einlesen
    with IBQBeitragRedakteur do
    begin
    Active := True;
    FetchAll;
    end;
    // zuletzt ausgewählte Publikation wieder aktivieren
    with IBQBeitragPublikation do
    begin
    Active := True;
    DisableControls;
    try
    while (IBQBeitragPublikationREIHEID.Value <> FCurrentPublikationID) and
    (not EOF) do
    Next;
    finally
    EnableControls;
    end;
    end;
    // zuletzt ausgewählte Ausgabe wieder aktivieren
    with IBDSSPBeitragAusgabe do
    begin
    Active := True;
    DisableControls;
    try
    while (IBDSSPBeitragAusgabeAUSGABEID.Value <> FCurrentAusgabeID) and
    (not EOF) do
    Next;
    finally
    EnableControls;
    end;
    end;
    // zuletzt ausgewählte Beitrag wieder aktivieren
    with IBDataSetBeitrag do
    begin
    Active := True;
    DisableControls;
    try
    while (IBDataSetBeitragBEITRAGID.Value <> FCurrentBeitragID) and
    (not EOF) do
    Next;
    finally
    EnableControls;
    end;
    end;
    with IBQBeitragManuskript do
    begin
    Active := True;
    FetchAll;
    end;
    IBQBeitragEditRedakteur.Active := True;
    // S-Locks entfernen und IdleTimer aktivieren
    IBTransactionMain.CommitRetaining;
    IBTransactionMain.IdleTimer := cIBDSBEITRAG_IdleTimer;
    FLocateMode := False;
    end;

    </pre>
    P.S: Detaillierter wird das Ganze auch in meinem Buch <i>InterBase-Datenbankentwickklung mit Delphi</i> beschrieben

    Comment


    • #3
      Hallo Hr. Kosch,<br><br>
      besten Dank für Ihre Hinweise. Ich habe Ihre Bücher und werde mir den Abschnitt durchlesen.<br>
      <br>
      Mir fehlt allerdings noch die Ursache für das Problem.<br>
      Kann man allgemein sagen, dass mit IBX meine Transaktion "lesen" zwar gestartet wird, aber durch das fehlende Commit und meinen Isolationslevel "Snapshot" nichts von weiteren Änderungen erfährt? Dann hätte also das Setzen des OnIdleTimer-Intervalls nur den Sinn, dass ein regelmässiges Commit (oder besser CommitRetaining?) ausgeführt wird?<br>
      Im obigen Beispiel fragen Sie ab, ob die Datenmenge im Status dsBrowse ist. Bei mir füllt diese Transaktion "_lesen" nur das Auswahlgrid, ist also immer im dsBrowse. Dann könnte ich auch den OnIdleTimer fest auf 10000 lassen. Das würde dann bedeuten, dass alle 10 Sekunden die Datenmenge geschlossen und wieder geöffnet wird. Habe ich dann ab einer bestimmten Menge je nach Selektion Performanceprobleme?<br>
      <br>
      Ist es überhaupt sinnvoll, für lesende und schreibende Zugriffe zwei verschiedene TIBTransaction-Objekte zu nutzen? Als Alternative könnte ich mir noch vorstellen, die Auswahlmenge in ein Stringgrid zu lesen und dieses nach Bedarf manuell zu aktualisieren.<br>
      <br>
      Vielen Dank für Ihre Hilfe,<br>
      Christian Storfinge

      Comment


      • #4
        Hallo Hr. Kosch,<br><br>
        ich habe gestern den IdleTimer auf 10000 gesetzt und rufe im dazugehörigen Ereignis ein CommitRetaining auf. Ich starte jetzt manuell die Lesen-Transaktion und setze diese sogar nach dem Einfügen auf active:=false und wieder active:=true sowie nach dem FetchAll sofort ein CommitRetaining.<br>
        <br>
        Leider ändert das nichts an der Tatsache, dass ein neuer Datensatz erst beim Neustart der Anwendung sichtbar ist.<br>
        <br>
        Noch eine Frage: Wieso bezeichnen Sie Locate als effizienter? Nach der Delphi-Dokumentation und diversen anderen Büchern gibt es keine schnellere Möglichkeit als Locate...<br>
        <br>
        Besten Dank für Ihre Bemühungen,<br>
        Christian Storfinge

        Comment


        • #5
          Eigentlich ist das ganz einfach du must die Transaction die für dei Grid zuständig ist restarten (Active:=False Active:=True) und da sind die Datensätze leider must du danach auch den Dataset wieder Activieren und warum nutzt du nich einfach TIBSQL oder willst du in der Grid auch daten ändern ?

          Comment


          • #6
            Hallo,
            <br>
            <br>ich möchte nicht direkt in die Diskusion eingreifen sondern zu <br>nächst noch eine etwas andere Methode vorstellen:
            <br>http://www.entwickler-forum.de\WebX?14@@.ee868b5
            <br>Diese gehört wohl zu dem Punkt:
            <br>>c) Verzicht auf TDBGrid kommt nicht in Frage, Mehraufwand für In-Memory-
            <br>>Tabellen kommt nicht in Frage. In regelmässigen Abständen wird
            <br>>CommitRetaining aufgerufen - wobei IBX die Datenmenge offen hält und
            <br>>sich die aktuellen Datensatzposition nicht ändern.
            <br>
            <br>Zu beachten ist hier die Warnungen von A.Kosch in #3:
            <br>"... der Isolationsgrad READ COMMITTED (Committed-Lesen) wird im SQL-Standard nur als Kompromiss betrachtet, da er gegen eine der Transaktionsregeln verstösst (innerhalb der eigenen, laufenden Transaktion sollte der Datenbestand immer so zu sehen sein, wie er beim Start der Transaktion war). Somit ist für puristische SQL-Fans anstelle von READ COMMITTED der Level REPEATABLE READ (alias SNAPSHOT beim InterBase) die 1. Wahl, denn nur hier werden die Anforderungen aus dem Standard erfüllt. Der InterBase muss intern bei READ COMMITTED mehr arbeiten als bei SNAPSHOT, so dass es ein Abwägung zwischen Aufwand und Nutzen ist."
            <br>
            <br>mfg
            <br>P

            Comment


            • #7
              Hallo zusammen!<br>
              <br>
              Ich habe wohl nicht bedacht, dass auch nur-lesende Transaktionen committed werden müssen. Nachdem ich in meinem Auswahlgrid nicht editieren muß/möchte, werde ich wohl auf ein StringGrid umstellen und dieses manuell neu füllen, wenn ich es für nötig halte.<br>
              <br>
              @Frank: Ich habe den "Restart" der lesenden Transaktion schon implementiert. Die neuen Datensätze erscheinen trotzdem nicht. Ich verwende im Transaktionsobjekt "Snapshot", das erscheint mir am sichersten.<br>
              <br>
              Gruß, Christia

              Comment


              • #8
                Wenn ich mich noch richtig erinnere (ich benutze kein TIBDataset mehr) giebt es in einigen Version von IBX ein Bug in der IBDataset was genau dein Problem beschreibt (ich weis nicht genau ob es das wahr) und ob es noch in der Aktuellen Version drin ist.
                Deshalb teste doch mal die Grid mit einer TIBSQL zu fülle

                Comment


                • #9
                  Hallo Frank,<br><br>
                  ich realisiere jetzt mein Auswahlgrid mit einem TStringGrid und aktualisiere wenn nötig.<br>
                  <br>
                  Du füllst Grids mit TIBSQL?<br>
                  Meines Wissens ist doch TIBSQL eine Einbahnstraße zum Server z.B. für insert- oder update-Statements. Ich kann doch damit keine Ergebnismenge öffnen mit active:=true, oder?<br>
                  <br>
                  Christia

                  Comment


                  • #10
                    Tschuldige ich meine TIBQuer

                    Comment


                    • #11
                      Hallo zusammen!<br>
                      <br>
                      Ich habe mein Auswahlgrid umgebaut. Ich habe jetzt (doch) noch ein TDBGrid, gefüllt von einer TIBQuery und jetzt ein neues Problem mit dieses Kombination.<br>
                      <br>
                      Dazu mache ich gleich einen neuen Thread mit dem Titel "Seltsames Verhalten von TIBQuery bei Locate" auf...<br>
                      <br>
                      Christia

                      Comment

                      Working...
                      X