Announcement

Collapse
No announcement yet.

Tabellenfeld automatisch füllen

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

  • Tabellenfeld automatisch füllen

    Hallo,

    in meiner Tabelle soll die Kundennummer nur bei Aktivierung einer CheckBox automatisch vergeben werden (andernfalls externe Vergabe). Es soll dann die größte vorhandene Nummer jeweils um 1 erhöht werden. Ist noch keine Kundennummer vorhanden soll mit einem vorgegebenen Startert begonnen werden. Wie kann ich das realisieren. Ich verwende Delphi 5, eine InterBase Datenbank und ADO-Komponenente.

    Danke schon mal im Voraus

    Gruß Elke

  • #2
    Hy Elke,
    das nachschauen in einer Datenbank nach dem höchsten Feld ist normlalerweise langweilig und Zeitaufwendig. Ich würde Den Counter auf einer eigenen DB ablegen und 2 Dinge tun den Counter holen, sperren, per index auf den letzten Eintrag in deiner Datenbank schauen ob Counter < dein neue Counter denke auch an gelöschte Einträge. und setzte dan deinen neuen Eintrag, Zentraler Counter inc 1 und freigeben. Beginn wenn noch kein Eintrag ist der Startcounter in der Zentralen Counter DB.

    Ich hoffe es hilft Dir

    Gruß Joachi

    Comment


    • #3
      Hallo Joachim,
      das hört sich für mich doch recht kompliziert an. Kann ich nicht in meinem Tabellefeld über den SQL Befehl select max den größten wert wählen und dann ab diesem Wert über inc1 weiterzählen ? Ich bin leider nicht so versiert in SQL, als dass ich das alleine hinkriegen würde.
      Gruß Elk

      Comment


      • #4
        Hallo Joachim,

        ich hab mal ein bischen rumprobiert und bin zu folgendem Ergebnis gekommen:

        In einer ADODataSet Komponente füge ich folgenden SQL-Code ein:

        select max(cast(kundenid as integer)) from Kundenstamm

        und in meinem Programm folgende Procedur:

        procedure TKundeStammForm1.CheckBox1Click(Sender: TObject);
        var
        id : integer;

        begin

        if CheckBox1.Checked = true then

        begin

        DataModule1.ADOConnection1.Connected := true ;

        id := DataModule1.ADODataSet2.FieldbyName('max').Value + 1;

        DataModule1.ADODataSet3.FieldbyName('kundenid').Va lue := id;

        end

        end;

        .. und siehe da, es funktioniert.

        Trotzdem vielen Dank für Deine Anregungen

        Gruß Elk

        Comment


        • #5
          Hallo Elke,

          wenn Dein Programm mehrbenutzerfähig sein soll, dann kann ich Dir von Deinem Weg nur ganz dringend abraten.

          Auf die Spalte Kundennummer ist doch hoffentlich ein eindeutiger Index eingerichtet (damit zumindest die Eintragung von doppelten Kundennummern verhindert wird).

          Wenn zwei Benutzer quasi paralell einen neuen Datensatz einfügen wollen kann es passieren, dass beide beim ermittelten der höchsten Kundennummer den gleichen Wert erhalten und damit Krachst natürlich in der IB-Datenbank.

          In IB sind für eindeutige Spalten die <b>Generatoren</b> vorgesehen. Sie liefern in jedem Fall eindeutige Werte (solange der Wertebereich von 64-Bit Zahlen (ab IB6.0) nicht überschritten wird). Sichergestellt wird das durch die Transaktionsunabhängigkeit der Generatoren).

          Dann würde ich in der IB-DB enen Trigger definieren der sich automatisch vom Generator einen neuen Wert abholt, wenn die Kundennummer noch nicht vorgegeben wurde.

          Tschüß

          Torste

          Comment


          • #6
            da hast Du natürlich recht, den Mehrbenutzerzugriff habe ich gar nicht bedacht. Kannst Du mir sagen wie ich das genau mache ? Da wäre ich Dir sehr dankbar.

            Gruß Elk

            Comment


            • #7
              Hallo Elke,

              wieso verwendest Du eigentlich ADO in Verbindung mit Interbase? ADO macht nur Sinn, wenn Dein Programm auch mit anderen SQL-Servern zusammenarbeiten soll.

              Den Generator erzeugst Du mit <b> create generator gen_kundennummer</b>.
              Den Startwert setzt Du mit <b> set generator gen_kundennummer to 1 </b>.

              Dann erzeugst Du Dir einen Trigger, z.B. <b><pre>
              CREATE TRIGGER Set_Kundenummer FOR Table_Kundendaten
              BEFORE INSERT
              as
              begin
              if (new.kundennummer is null) then new.kundennummer = gen_id(gen_kundennummer, 1);
              end
              </b>
              </pre>
              Die manuelle Eingabe der Kundennummer würde ich mir an Deiner Stelle noch einmal gut überlegen. Du handelst Dir damit nur zusätzlichen Aufwand ein (mit der manuellen Eingabe umgehst Du ja die automatische und eindeutige Vergabe der Kundennummer).

              Der Trigger wird nur dann richtig funktionieren, wenn ADO bzw. der dahinterliegende OLE-DB Treiber bei einer leeren Kundenummer den Zustand <b> null</b> in den Datensatz einträgt. Eventuell wird statt dem Zustand "null" (bedeutet: Feld hat keinen Inhalt) der numerische Wert 0 eingetragen. In diesem Fall muß der Trigger auf 0 testen (if (new.kundennummer = 0) then ...).

              Tschüß

              Torste

              Comment


              • #8
                Hi Torsten,

                erst mal vielen Dank für Dein Beispiel. Die manuelle Vergabe der Kundennummer muss allerdings sein, da eine Firma ja auch schon bestehende Kunden hat, deren Kundennummer nicht so ohne weiteres geändert werden kann.

                Ich hab mir nun überlegt, die Funktion in einer Stored Procedure zu realisieren, die nur bei anklicken der CheckBox aufgerufen wird. Wäre das Deiner Meinung nach möglich ? Und könnte man nicht evt. den Schreibzugriff während dieser Art der Vergabe für andere User sperren und anzeigen lassen, wer gerade den Kunden bearbeitet? Es werden ja auch nicht jede Minute von jedem User neue Kunden angelegt.

                Es wäre toll, wenn Du mir bei der Lösung helfen könntest

                Gruß Elk

                Comment


                • #9
                  Hallo Elke,

                  die StoredProc habe ich bereits im anderen Thread gezeigt. Wichtig ist, dass vor dem Aufruf der StoredProc eine neue Transaktion gestartet wird.

                  Das einfügen des neuen Datensatzes wird dann mit einem <b>try...except - Block</b> geschützt und in dem <b>except Abschnitt</b> auf den SQL-Fehlercode -803 getestet. Tritt dieser Fehler auf, ist die Kundennummer bereits vorhanden und die Transaktion muß mit Rollback zurückgesetzt werden. Danach kann die Aktion wiederholt werden.

                  Das Ganze könnte so aussehen:

                  <pre><b>
                  var
                  neue_kundenummer: integer;
                  ok: boolean;
                  begin
                  repeat
                  ok := true;
                  if StoredProc_KundenID.InTransaction then StoredProc_KundenID.Transaction.committ;
                  StoredProc_KundenID.Transaction.StartTransaction;
                  StoredProc_KundenID.execute
                  neue_kundennummer := StoredProc_KundenID.FieldByName('ID_NEU');
                  try
                  Code für neuen Datensatz einfüguen
                  except
                  auf SQL-Fehler -803 testen
                  ok := false;
                  StoredProc_KundenID.Transaction.Rollback;
                  end;
                  until ok;
                  if StoredProc_KundenID.InTransaction then StoredProc_KundenID.Transaction.committ;
                  end;
                  </pre></b>

                  In die Schleife sollte noch ein Wiederholungszähler eingebaut werden der bei überschreiten einer bestimmten Grenze eine Fehlermeldung ausgibt (Vermeidung einer Endlosschleife).

                  Obige Syntax wird wahrscheinlich nicht korrekt sein, aber ich habe die ADO-Komponenten nicht zur Verfügung und auch keine Erfahrungen damit (Habe nur Erfahrungen über die Zugriffswege IBX, IBO und BDE-SQLLinks). Deswegen kann ich Dir auch nicht zeigen wie der Fehler konkret "abgefangen" werden muß. Das schaffst Du bestimmt auch ohne mich.

                  Tschüß

                  Torste

                  Comment


                  • #10
                    Hallo Torsten,

                    Danke für Deine Antwort, aber ich habe immer noch Probleme mit meiner Stored Procedure. Schau mal bitte in meinem SQL-Thread nach. Ich hoffe Du kannst mir da helfen.

                    Gruß Elk

                    Comment


                    • #11
                      Hallo Torsten,

                      ich hab mich endlich mal der Stored Procedure angenommen und sie im Programm so implementiert, wie Du es beschrieben hast. Das funktioniert auch alles wunderbar ... beim ersten mal anklicken. Wenn nun allerdings der Anwender aus welchen Gründen auch immer die Checkbox deaktivieren und wieder aktivieren will, erscheint eine Fehlermeldung. Das gleiche würde wahrscheinlich auch passieren, wenn ein anderer Anwender zur gleichen Zeit darauf zugreifen will.

                      Eine allgemeine Frange zu Transaktionen:
                      über ADO kann ich den Status der Transaktion nur über die Connection-Komponente setzen ( wenn ich da nichts übersehen hab). D.h. dass, wenn ich vor dem Start der Stored Procedure eine bestehende Transaction beende, ja auch die Insert Transaktion beendet werden würde, da die Stored Procedure ja in dieser geschachtelt ist. Wenn ich das richtig verstehe, dass Delphi für jeden Datenbankzugriff automatisch eine neue Transaktion startet. Das würde heißen, dass ich die if bedingung gar nicht bräuchte, sondern einfach nur eine neue Transaktion starten müsste und diese bei beendigung der Schleife mit Commit bzw. Rollback beenden müsste.

                      Ist es eigentlich besser die Transakionsverwaltung über Delphi laufen zu lassen, oder sollte man das selbst in die Hand nehmen ?
                      Hier noch die Procedur, wie sie für ADO aussehen würde:

                      procedure TKundeStammForm1.CheckBox1Click(Sender: TObject);

                      var

                      id_neu : string;

                      ok : boolean;

                      begin

                      if CheckBox1.Checked = true then

                      begin

                      DataModule1.ADODataSet3.FieldByName('kundenid').Va lue := null;

                      repeat

                      ok := true;

                      if DataModule1.ADOConnection1.InTransaction then

                      DataModule1.ADOConnection1.commitTrans;

                      DataModule1.ADOConnection1.BeginTrans;

                      DataModule2.ADOStoredProc_KundenID.execProc;

                      id_neu := DataModule2.ADOStoredProc_KundenID.FieldByName('ID _NEU').Value;

                      try

                      DBEdit12.Field.ASString := id_neu

                      except

                      {on EADOError -803 DO }
                      ok := false;

                      DataModule1.ADOConnection1.RollbackTrans;

                      end;

                      until ok;

                      if DataModule1.ADOConnection1.InTransaction then

                      DataModule1.ADOConnection1.commitTrans;

                      end;

                      end;

                      Wie ich mit ADO auf den SQLCode teste weiß ich auch noch nicht, vielleicht hat ja dafür jemand eine Lösung parat.

                      So, das war jetzt ganz schön viel. Ich hoffe dass da jemand mir etwas Licht ins Dunkel bringen kann.

                      Gruß Elk

                      Comment


                      • #12
                        Hallo Elke,

                        ich würde für die Storedproc eine separate Transaktion verwenden und mich nicht auf die automatische Transaktionssteurung von Delphi/ADO verlassen. Nur so hast Du als Entwicklerin volle Kontrolle über die Transaktion (Stichworte Isolationslevel, lockwait usw.).

                        Die Überprüfung ob die Transaktion aktiv ist dient nur der Programmstabilität. Gerade wenn eine Transaktionskomponente im mehreren Programmteilen verwendet wird kann es schnell passieren das der erwartete Status der Transaktion nicht vorgefunden wird (hervorragende Fehlerquelle). Das Ganze spielt natürlich nur eine Rolle wenn in die automatische Transaktionsteuerung von Delphi/ADO eingegriffen wird.

                        Um die Fehlermeldung zu ermitteln brauchst Du doch nur den Fehler zu provozieren indem du eine bereits vorhandene Kundenummer noch einmal einfügst. Im Exception-Teil schaust Du dann welche Exception ausgelöst wurde.

                        Tschüß

                        Torste

                        Comment


                        • #13
                          ok, ich hab die Procedure jetzt ohne die if Bedingung eingesetzt und erhalte keine Fehlermeldung mehr.

                          Gruß Elk

                          Comment


                          • #14
                            Hallo,

                            nachdem beim ersten mal nun alles so toll geklappt hat, ergeben sich bei der weiteren Anwendung doch wieder ein paar Probleme (es hätte mich ja auch gewundert). Wenn ich nun zu ersten mal in der Anwendung einen Kunden einfüge und die Kundennummer automatisch füllen lasse ist noch alles in Ordnung. Will ich direkt darauf aber noch einen Kunden anlegen gibt mir die Stored Procedure wieder die gleiche Kundennummer wie vorhin aus. Ich hab die Stored Procedure sowie die Post Anweisung wieder mit Commit bzw. Rollback geschlossen und die ADODatSet-Komponente neu geöffnet. Aber scheinbar ist der Datensatz in der Tabelle trotzdem noch nicht aktualisiert. Was muss ich noch beachten, dass die Daten vor jedem Aufruf aktualisiert sind ? Bei einem Neustart des Programms ist natürlich wieder alles in bester Ordnung.

                            Gruß Elk

                            Comment


                            • #15
                              Hallo Elke,

                              wenn Du die Datenänderung (post) mit commit bestätigst wird sie endgültig in die Datenbank übernommen. Wenn Du jetzt eine neue Transaktion startest und die StoredProc aufrufst muß der neue Datensatz sichtbar sein!

                              folgende Schritte müssen durchlaufen werden:
                              <pre>
                              1. Transaktion starten (für die StoredProc)
                              2. StoredProc aufrufen -> höchste Kundennummer + 1
                              3. Transaktion beenden - commit (für StoredProc)
                              4. Transaktion starten - (für die Datenänderung)
                              5. Datensatz einfügen
                              6. Transaktion beenden - commit (für die Datenänderung)
                              </pre>
                              wenn Du jetzt Punkt 1. und 2. wiederholst muß die höchste Kundennummer sich geändert haben.
                              <p>
                              In Abhängigkeit vom Isolationslevel der Transaktionen könnte man einige Zwischenschritte überspringen, aber so muß es auf jeden Fall funktionieren.

                              Tschüß

                              Torste

                              Comment

                              Working...
                              X