Announcement

Collapse
No announcement yet.

Doppelte Belegnummern

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

  • Doppelte Belegnummern

    Im Rahmen eines kaufmännischen Projektes habe ich eine StoredProc geschrieben, die eine Belegnummer aus einer NUMMERN-Tabelle holt, um eins erhöht, wegschreibt und zurückgibt. Die Procedure sieht im Prinzip wiefolgt aus.

    DECLARE @LFDNR INTEGER;
    DECLARE @NEWNR INTEGER;

    @LFDNR=(SELECT LastNummer from NUMMERN);
    @NEWNR=@LFDNR+1;

    UPDATE NUMMERN SET LastNummer=@NewNR;
    INSERT INTO __Output (FNUMMER) Values (@NewNr);

    Mein Problem ist, daß im Netz ( insgesamt 8 User) es sporadisch dazu kommt, daß zwei Benutzer die gleiche Nummer zurück bekommen. Im Programm selbst wird die Procedure in der BeforeInsert-Methode des AdsTable-Objektes aufgerufen.

    Mir ist nicht ganz klar wo ich ansetzen soll um das Problem zu lösen ? Benutzt wird der ADS 8.1.

  • #2
    Ich kenne den ADS nicht, aber die meisten Datenbanken kennen so etwas wie 'locking', d.h. vor deinem Code sperrst du die Tabelle, führst deinen Code aus und gibst die Tabelle dann wieder frei. Es kann damit nur ein User zur selbsen Zeit auf die DB zugreifen und somit würdest du das Problem in den Griff bekommen.
    Ich hoffe, dass das hilft...
    "A common mistake that people make
    when trying to design something completely foolproof
    is to underestimate the ingenuity of complete fools.
    "
    >>> Douglas Adams, Mostly Harmless
    -------------------------------------------------------
    Techcrawler.de | JaMT | deCHK | Extra-Bonus-Shopping.de | Scour

    Comment


    • #3
      Das ist ja richtig, wenn ich das über den Client steuern will oder muss. Eine StoredProc wird aber auf dem Server ausgeführt. Die Anfragen werden hintereinander abgearbeitet und der Server sollte das dann selber regeln.

      Comment


      • #4
        Hallo Harald,

        was du brauchst ist ein Pessimistic Locking auf der Nummern-Tabelle. D.h. wenn die laufende Nummer geholt wird, darf solange kein anderer von der Nummer-Tabelle lesen, bis der Erste fertig ist. Quasi ein serialisierter Zugriff. Unterschiedliche DBMS, unterschiedliche Möglichkeiten. Ich weiss nicht genau, ob z.B. ADS ein WITH LOCK bei einem SELECT unterstützt.

        Was du versuchen könntest ist, innerhalb einer Transaktion:

        - ein Dummy-Update auf die Nummern-Tabelle machen (z.b. update nummerntabelle set lastnummer=lastnummer)
        - LastNummer + 1 mit SELECT holen
        - diese Nummer in deiner Logik verwenden
        - LastNummer + 1 zurückschreiben
        - Transaktion committen

        Vielleicht klappt das auch beim ADS. Vielleicht hat ja sonst Joachim noch eine Idee, wie man das serverseitig macht.


        Thomas
        Thomas Steinmaurer

        Firebird Foundation Committee Member
        Upscene Productions - Database Tools for Developers
        Mein Blog

        Comment


        • #5
          Hi Thomas,
          deine Antwort ist im Prinzip schon richtig. Ein SELECT FOR UPDATE gibt es nicht, aber mit Transaktionen und einem Dummy kann man das kapseln.
          Soll es auch ohne Transaktionen funktionieren, so würde ich an die Nummern-Tabelle ein Feld Rowversion anhängen. Das erhöht sich bei jedem Update und kann zur Prüfung verwendet werden, ob ein anderer Benutzer in der Zwischenzeit die Finger dran hatte.

          @numrows=0;
          while @numrows=0 do
          @rv=(select rowver from idtable where tbname like @tbname);
          @nextid=(select lastid+1 from idtable where tbname like @tbname);
          try
          update idtable set lastid=@nextid where tbname like @tbname and rowver=@rv;
          @numrows=(select ::stmt.UpdateCount from system.iota);
          catch all
          @numrows=0;
          end try;
          end while;

          Comment


          • #6
            Danke für die Antworten. Ich werde mal ein paar Tests starten.

            Comment

            Working...
            X