Eindeutige Nummer erzeugen. [ERLEDIGT]

Advantage Database Server

Moderator: Moderatoren

Antworten
UliTs
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2828
Registriert: Fr, 10. Feb 2006 9:51
Wohnort: Aachen
Hat sich bedankt: 259 Mal
Danksagung erhalten: 12 Mal
Kontaktdaten:

Eindeutige Nummer erzeugen. [ERLEDIGT]

Beitrag von UliTs »

Hallo allerseits,

mit folgender Funktion im Data Dictionary möchte ich eindeutige Id's je IdBez vergeben. In IdVerwaltung werden die Id's für verschiedene IdBez's gespeichert. Die Eindeutigkeit klappt aber nicht immer.

Wie könnte man sie besser schreiben?

Code: Alles auswählen

    CREATE FUNCTION NaechsteId
       (
       cIdBez CHAR ( 20 )
       )
       RETURNS INTEGER
    DESCRIPTION 'Es wird aus IdVerwaltung LetzteId zu cIdBez gelesen, der Wert um 1 erhöht und als Ergebnis zurückgegeben.
    Um sicherzustellen, daß die Id in einer Tabelle noch nicht vergeben wurde, kann PruefLetzteId() verwendet werden.
    Aus Geschwindigkeitsgründen wird PruefLetzteId() nicht von NaechsteId() aufgerufen!'
    BEGIN

    DECLARE nReturn Integer;
      SET nReturn =
        ( SELECT LetzteId
          FROM   IdVerwaltung
          WHERE  IdBez=cIdBez )+1;
      IF nReturn IS NULL THEN
        INSERT INTO IdVerwaltung(IdBez,LetzteId) VALUES(cIdBez,0 );
        SET nReturn = 1;
      ENDIF;
      UPDATE IdVerwaltung
         SET LetzteId=nReturn
       WHERE IdBez=cIdBez;
    RETURN nReturn;

    END;
Zuletzt geändert von UliTs am Di, 04. Feb 2014 0:45, insgesamt 1-mal geändert.
-------
Mitglied XuG Cologne
Mitglied XuG Osnabrück
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von brandelh »

Hat die ADS kein autoincrement Feld ?
Gruß
Hubert
Benutzeravatar
Tom
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 9356
Registriert: Do, 22. Sep 2005 23:11
Wohnort: Berlin
Hat sich bedankt: 101 Mal
Danksagung erhalten: 361 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von Tom »

Hat die ADS kein autoincrement Feld ?
Die ADS unterstützt DBF (klassisch und Fox) und ADT. Fox und ADT kennen Autoincrement-Felder, das klassische DBF nicht. Es hängt also nicht an der ADS, sondern am Tabellenmodell. :wink:
Herzlich,
Tom
Benutzeravatar
Tom
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 9356
Registriert: Do, 22. Sep 2005 23:11
Wohnort: Berlin
Hat sich bedankt: 101 Mal
Danksagung erhalten: 361 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von Tom »

Hallo, Uli.

Wenn ich Deinen Code richtig verstehe, kann die Tabelle "IdVerwaltung" Redundanzen enthalten, weil "IdBez" mehrfach auftreten kann und am Ende des Statements komplett auf den letzten Zähler aktualisiert wird, also für jedes Auftreten. Nach meine Verständnis kann das SELECT vorher aber auch andere Daten zurückreichen, wenn es zu einem "IdBez" mehrere unterschiedliche Werte gibt. Oder nicht? Gibt es einen UNIQUE-Index auf der fraglichen Tabelle?
Herzlich,
Tom
Benutzeravatar
brandelh
Foren-Moderator
Foren-Moderator
Beiträge: 15695
Registriert: Mo, 23. Jan 2006 20:54
Wohnort: Germersheim
Hat sich bedankt: 65 Mal
Danksagung erhalten: 33 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von brandelh »

mit der Syntax im ADS kenne ich mich nicht aus, aber in einem DevCon Vortrag von Steffen hat dieser erklärt,
dass man bei gesharten Dateien nur mit dieser Vorgehensweise eine sichere eindeutige ID erzeugen kann !

Code: Alles auswählen

* Index steht auf ID
if flock() // FILELOCK verhindert, dass andere Stationen sperren können  :!: 
   dbGoBottom()
   nNeueNr := field->ID + 1
   dbAppend()
   replace ID with nNeueNr
   ...
   UNLOCK
   dbGoTop() // Puffer neu einlesen
endif
Falls du nicht die Datei sperren kannst, musst du die komplette Aktion in eine Transaktion packen, diese sollte sicherstellen, dass gleichzeitig keiner eine andere Nr ermittelt.
Gruß
Hubert
Benutzeravatar
nightcrawler
1000 working lines a day
1000 working lines a day
Beiträge: 650
Registriert: Di, 24. Apr 2012 16:33
Wohnort: 72184 Weitingen
Hat sich bedankt: 3 Mal
Danksagung erhalten: 96 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von nightcrawler »

man sollte zumindest sicherstellen, dass die Funktion atomar ist...entweder über spezielle Felder (bei ADT zB RowVersion Typ), oder aber über Transaktionssicherung (Transaktion starten, Datensatz updaten ==> gesperrt, Operation durchführen, Neuen Wert zurückschreiben, Transaktion abschliessen).
--
Joachim
Joachim Dürr Softwareengineering
https://www.jd-engineering.de
UliTs
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2828
Registriert: Fr, 10. Feb 2006 9:51
Wohnort: Aachen
Hat sich bedankt: 259 Mal
Danksagung erhalten: 12 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von UliTs »

Tom hat geschrieben:Wenn ich Deinen Code richtig verstehe, kann die Tabelle "IdVerwaltung" Redundanzen enthalten, weil "IdBez" mehrfach auftreten kann und am Ende des Statements komplett auf den letzten Zähler aktualisiert wird, also für jedes Auftreten. Nach meine Verständnis kann das SELECT vorher aber auch andere Daten zurückreichen, wenn es zu einem "IdBez" mehrere unterschiedliche Werte gibt. Oder nicht? Gibt es einen UNIQUE-Index auf der fraglichen Tabelle?
Hallo Tom,
ich bin mir nicht sicher, ob ich Dich richtig verstehe: Mit "IdBez" gibt es keine Probleme. Wenn es eine Bezeichnung in IdVerwaltung noch nicht gibt, wird ein neuer Datensatz und damit ein neuer Id-Nummernkreislauf angelegt. Ich vermute vielmehr, dass die Funktion in verschiedenen Threads parallel ausgeführt wird und es dann vereinzelt bei gleicher "IdBez" zur Doppelvergabe kommt.

Hallo Hubert,
ja, file locking wäre eine Lösung. Auch record locking würde meines Erachtens reichen.

Hallo Joachim,
ich schau mal als Erstes, ob ich etwas über atomare Funktionen finde. Das wäre meines Erachtens das Einfachste (und auch schnellste?).

Uli
-------
Mitglied XuG Cologne
Mitglied XuG Osnabrück
UliTs
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2828
Registriert: Fr, 10. Feb 2006 9:51
Wohnort: Aachen
Hat sich bedankt: 259 Mal
Danksagung erhalten: 12 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von UliTs »

Also ich bin kläglich gescheitert ...

Locking gibt es in SQL-Anweisungen nicht.

Die Tabelle muß ich mit Flag "Transaktion Free" benutzen, da es sonst bei sehr großen Transaktionen zu Problemen und im konkreten Fall zu "Dead Locks" kommen könnte.

Das Einzige was ich in der Dokumentation zu "atomar" -> "atomic" gefunden habe, dass man -wenn dies benötigt wird- mit Transaktionen arbeiten soll. Wenn ich der Funktion die Eigenschaft "atomar" geben könnte, wäre das Problem gelöst ... Aber wie?

Ich habe noch eine Idee, bei der eventuell nicht alle Nummern vergeben werden. Das wäre im konkreten Fall aber nicht so schlimm.

Uli
-------
Mitglied XuG Cologne
Mitglied XuG Osnabrück
UliTs
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2828
Registriert: Fr, 10. Feb 2006 9:51
Wohnort: Aachen
Hat sich bedankt: 259 Mal
Danksagung erhalten: 12 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von UliTs »

UliTs hat geschrieben:Ich habe noch eine Idee, bei der eventuell nicht alle Nummern vergeben werden. Das wäre im konkreten Fall aber nicht so schlimm.
So hier ist nun die Funktion:

Code: Alles auswählen

FUNCTION NaechsteId 
   ( 
   cIdBez CHAR ( 20 )
   )
   RETURNS INTEGER
DESCRIPTION 'Es wird aus IdVerwaltung LetzteId zu cIdBez gelesen, der Wert um 1 erh÷ht und als Ergebnis zur³ckgegeben.
Um sicherzustellen, da¯ die Id in einer Tabelle noch nicht vergeben wurde, kann PruefLetzteId() verwendet werden.
Aus Geschwindigkeitsgr³nden wird PruefLetzteId() nicht von NaechsteId() aufgerufen!'
BEGIN
DECLARE nReturn    Integer;
DECLARE nNewReturn Integer;
DECLARE lOk        Logical;
  SET lOk = FALSE;
  WHILE TRUE DO
    TRY
      SET nReturn =
        ( SELECT LetzteId
          FROM   IdVerwaltung
          WHERE  IdBez=cIdBez )+1;
      IF nReturn IS NULL THEN
        INSERT INTO IdVerwaltung(IdBez,LetzteId) VALUES(cIdBez,0 );
        SET nReturn = 1;
      ENDIF;
      UPDATE IdVerwaltung
         SET LetzteId=LetzteId+1
       WHERE IdBez=cIdBez;
      SET nNewReturn =
        ( SELECT LetzteId
          FROM   IdVerwaltung
          WHERE  IdBez=cIdBez );
      IF nNewReturn IS NUll THEN
        SET lOk = FALSE;
      ELSE
        SET lOk = (nReturn = nNewReturn);
      ENDIF;
    CATCH ALL
      SET lOk = FALSE;
    END TRY;
    IF lOk THEN
      LEAVE;
    ENDIF;
  END WHILE;
RETURN nReturn;

END;
Einziges mögliches Problem: wenn 2 Threads exakt gleichzeitig die Funktion mit gleichem Parameter ausführen, kann es zumindest theoretisch zu einer Endlosschleife kommen ...

Uli
-------
Mitglied XuG Cologne
Mitglied XuG Osnabrück
Benutzeravatar
nightcrawler
1000 working lines a day
1000 working lines a day
Beiträge: 650
Registriert: Di, 24. Apr 2012 16:33
Wohnort: 72184 Weitingen
Hat sich bedankt: 3 Mal
Danksagung erhalten: 96 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von nightcrawler »

Uli,
Du arbeitest doch mit einem Data Dictionary...also kannst Du DBF und ADT mischen. Nimm doch eine ADT-Tabelle für die Nummern-Vergabe und hänge dort ein ROWVERSION Feld mit ein, welches Du zur Parallelitäts-Kontrolle verwendest.

Code: Alles auswählen

CREATE FUNCTION gen_id(cname cichar(255))
   RETURNS integer
BEGIN
    declare @numrows integer;
    declare @nextid integer;
	declare @rv integer;
    --create generator table if not exists
	if not exists(select * from system.tables where name like 'tb_generator') then
	  create table tb_generator(id autoinc, name cichar(255), lastid integer, rv rowversion);
	endif;
	--check if there's already a generator for that name
	if not exists(select * from tb_generator where name like cname) then
	  insert into tb_generator(name,lastid) values (cname,0);
	endif;
	--now update that record until you got it (concurrency!)
	@numrows=0;
    while @numrows=0 do
      @rv=(select rv from tb_generator where name like cname);
      @nextid=(select lastid+1 from tb_generator where name like cname);
      try
        update tb_generator set lastid=@nextid where name like cname and rv=@rv;
		--if there's no update, another user already updated that record
        @numrows=(select ::stmt.UpdateCount from system.iota);
      catch all
        @numrows=0;
      end try;
    end while;
	return @nextid;
END;
--
Joachim
Joachim Dürr Softwareengineering
https://www.jd-engineering.de
UliTs
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2828
Registriert: Fr, 10. Feb 2006 9:51
Wohnort: Aachen
Hat sich bedankt: 259 Mal
Danksagung erhalten: 12 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von UliTs »

Hallo Joachim,

ja, der Einsatz einer Spalte vom Typ "rowversion" ist die Lösung! Super Idee! Dabei wird keine Nummer ausgelassen!
Allerdings das theoretische Problem mit der Endlosschleife besteht weiterhin, oder? ...
Wenn ich es realisiert habe, melde ich mich nochmals.

Uli
-------
Mitglied XuG Cologne
Mitglied XuG Osnabrück
Benutzeravatar
nightcrawler
1000 working lines a day
1000 working lines a day
Beiträge: 650
Registriert: Di, 24. Apr 2012 16:33
Wohnort: 72184 Weitingen
Hat sich bedankt: 3 Mal
Danksagung erhalten: 96 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von nightcrawler »

UliTs hat geschrieben:ja, der Einsatz einer Spalte vom Typ "rowversion" ist die Lösung! Super Idee! Dabei wird keine Nummer ausgelassen!
Allerdings das theoretische Problem mit der Endlosschleife besteht weiterhin, oder?
Naja...es gewinnt auf jeden Fall imer einer, weil das Beschreiben atomar ist. Und der andere wartet halt und probiert es nochmal. Ich denke mal, dass ein Deadlock hier nicht vorkommen kann.
--
Joachim
Joachim Dürr Softwareengineering
https://www.jd-engineering.de
UliTs
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2828
Registriert: Fr, 10. Feb 2006 9:51
Wohnort: Aachen
Hat sich bedankt: 259 Mal
Danksagung erhalten: 12 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen.

Beitrag von UliTs »

nightcrawler hat geschrieben:Naja...es gewinnt auf jeden Fall imer einer, weil das Beschreiben atomar ist. Und der andere wartet halt und probiert es nochmal. Ich denke mal, dass ein Deadlock hier nicht vorkommen kann.
Du hast Recht! Die Problematik ist damit auch aus der Welt geschaffen :D .
Danke!

Uli
-------
Mitglied XuG Cologne
Mitglied XuG Osnabrück
UliTs
Der Entwickler von "Deep Thought"
Der Entwickler von "Deep Thought"
Beiträge: 2828
Registriert: Fr, 10. Feb 2006 9:51
Wohnort: Aachen
Hat sich bedankt: 259 Mal
Danksagung erhalten: 12 Mal
Kontaktdaten:

Re: Eindeutige Nummer erzeugen. [ERLEDIGT]

Beitrag von UliTs »

Ich glaube, ich habe es hinbekommen :) .
Vielen Dank für Deine Hilfe!

Uli
-------
Mitglied XuG Cologne
Mitglied XuG Osnabrück
Antworten