AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Datenbanken Letzte vergebene Nummer speichern
Thema durchsuchen
Ansicht
Themen-Optionen

Letzte vergebene Nummer speichern

Ein Thema von BlueStarHH · begonnen am 3. Aug 2022 · letzter Beitrag vom 7. Aug 2022
Antwort Antwort
Seite 2 von 4     12 34      
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#11

AW: Letzte vergebene Nummer speichern

  Alt 5. Aug 2022, 14:25
Die StoredProcedure kann natürlich von mehreren Benutzern aufgerufen werden. Ich möchte nun immer nur die zuletzt erzeugte ID in einer Tabelle speichern.
Es ist immer noch nicht ganz klar, was mit zuletzt erzeugt gemeint ist.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
BlueStarHH

Registriert seit: 28. Mär 2005
Ort: Hamburg
850 Beiträge
 
Delphi 11 Alexandria
 
#12

AW: Letzte vergebene Nummer speichern

  Alt 5. Aug 2022, 14:33
Die StoredProcedure kann natürlich von mehreren Benutzern aufgerufen werden. Ich möchte nun immer nur die zuletzt erzeugte ID in einer Tabelle speichern.
Es ist immer noch nicht ganz klar, was mit zuletzt erzeugt gemeint ist.
Ist wohl in meinem Post #8 etwas untergegangen dahier hier nochmal deutlicher:

Die ID mit dem größten Generatorwert. Dieser wird aber von Zeit zu Zeit auch wieder auf 1 gesetzt. Daher kann ich den nicht einfach so abfragen.
Wenn der Generator nicht benutzt wird, ist es prinipiell egal ob die letzte ID durch die zuletzt aufgerufene SP oder die zuletzt beendete SP festgelegt wird.

Geändert von BlueStarHH ( 5. Aug 2022 um 14:35 Uhr)
  Mit Zitat antworten Zitat
BlueStarHH

Registriert seit: 28. Mär 2005
Ort: Hamburg
850 Beiträge
 
Delphi 11 Alexandria
 
#13

AW: Letzte vergebene Nummer speichern

  Alt 5. Aug 2022, 14:42
Du kannst einen Generator nutzen:
...
Wenn Du eine neue ID erstellt hast, schreibst Du diese per Insert insert into LastID (DeineID) values (DerWertDerID); in diese Tabelle.
Wann rufe ich das insert into auf? Wie ist sichergestellt, dass das insert into mit der zuletzt erzeugten ID aufgerufen wird? So klappt das z.B. *nicht*:


Code:
procedure GetNextID
  a := ErzeugePrefix;
 
  if Bedingung then
    b := gen_id(MeinGenerator, 1);
  else
    b := '';

  c := ErzeugeSuffix;

  result := a+b+c;

  insert into LastID (DeineID) values (DerWertDerID)
end;
Beispiel: Benutzer A erzeugt mit gen_id die Nr 1. Jetzt kommt schon Benutzer B und erzeugt die Nr 2 mit gen_id und schreibt diese mit insert into in die LastID-Tabelle. Nun wird erst die letzte Zeile der SP vom Benutzer A aufgerufen und schon steht die 1 als falsche letzte ID in der LastID-Tabelle.
  Mit Zitat antworten Zitat
BerndS

Registriert seit: 8. Mär 2006
Ort: Jüterbog
491 Beiträge
 
Delphi 12 Athens
 
#14

AW: Letzte vergebene Nummer speichern

  Alt 5. Aug 2022, 15:26
Ich habe ein ähnliches Problem auch gehabt.
Meine Lösung im groben:
Vor dem Aufruf der SP (die die Nummer erzeugt) wird ein Datensatz in einer Sperrtabelle eingefügt.
Diese Tabelle hat einen eindeutigen Index auf einen Feld.
Dadurch ist ein doppeltes Einfügen nicht möglich. z.B. 'GETBUCHNR'
Nach dem Insert in die Sperrtabelle wird die Nummer per SP erzeugt, wobei diese prüft ob auch die Sperre gesetzt ist.
Danach wird der Datensatz mit der Nummer entweder eingefügt oder die Nummer gesetzet. Nach dem Commit wird die Sperre entfernt.

Die Funktion, die die Sperre anlegt, ist so aufgebaut, dass sie es bis zu eimen definieten Timeout wiederholt mit kurzem Sleep versucht, den Datensatz einzufügen.
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.508 Beiträge
 
Delphi 7 Professional
 
#15

AW: Letzte vergebene Nummer speichern

  Alt 5. Aug 2022, 15:35
Was heißt denn das?

Dieser wird aber von Zeit zu Zeit auch wieder auf 1 gesetzt.
Mit System, eher zufällig?

Wenn der Generator zurückgesetzt wird, müssen zeitgleich auch alle Datensätze aus der Tabelle entfernt werden. Zur Erkennung des neuesten Satzes benötigst Du einen eindeutigen Wert, der die korrekte Erkennung sicherstellt. Wenn dieser Wert allerdings "wahlfrei" zurückgesetzt oder verändert wird, ist eine eindeutige Erkennung nicht mehr gegeben. Das spricht ganz klar für ein Designproblem.

Einen Generator, den man für die eindeutige Kennzeichnung von Werten nutzt, setzt man nicht zurück. Allenfalls dann, wenn die Gefahr eines Überlaufes besteht.

Der Wertebereich von Generatoren bei FireBird liegt zwischen
Code:
-2^63 .. 2^63
-9,223,372,036,854,775,808 .. 9,223,372,036,854,775,807
Wenn man nach dem Erstellen eines Generators den Wert auf -2^63 setzt, kann man damit schon 'ne ganze Weile auskommen. Selbst bei 1.000.000 IDs pro Sekunde könnte das dann mit fast 584.942,41735507203247082699137494 Jahren, auch bei optimistischer Betrachtung, die Laufzeit heutiger Software deutlich übersteigen

Wenn Du also heute einen entsprechenden Generator (wie im obigen Post bechrieben) erstellst, darfst Du ihn in ca. 584.000 Jahren auf -2^63 setzen.

Und: Derartige generatorgenerierte IDs werden nie, nie und nochmals nie für was anderes verwendet, als für die Feststellung, welches ist der neueste Wert. Es gibt also (für meine Begriffe) keinen vernunftbegabten Grund, weshalb man einen für diese Aufgabe genutzten Generator jemals wieder auf 1 setzen sollte. (Ok, ist etwas arg überspitzt formuliert )

---

Du kannst einen Generator nutzen:
...
Wenn Du eine neue ID erstellt hast, schreibst Du diese per Insert insert into LastID (DeineID) values (DerWertDerID); in diese Tabelle.
Wann rufe ich das insert into auf? Wie ist sichergestellt, dass das insert into mit der zuletzt erzeugten ID aufgerufen wird? So klappt das z.B. *nicht*:


Code:
procedure GetNextID
  a := ErzeugePrefix;
 
  if Bedingung then
    b := gen_id(MeinGenerator, 1);
  else
    b := '';

  c := ErzeugeSuffix;

  result := a+b+c;

  insert into LastID (DeineID) values (DerWertDerID)
end;
Beispiel: Benutzer A erzeugt mit gen_id die Nr 1. Jetzt kommt schon Benutzer B und erzeugt die Nr 2 mit gen_id und schreibt diese mit insert into in die LastID-Tabelle. Nun wird erst die letzte Zeile der SP vom Benutzer A aufgerufen und schon steht die 1 als falsche letzte ID in der LastID-Tabelle.
Hier haben wir genau das Problem, nach dem Uwe Raabe soeben gefragt hat.

Was ist jetzt die letzte ID. Die, die beim letzten Aufruf der SP (durch wen auch immer) generiert wird, oder die, die beim letzten Beenden der SP (durch wen auch immer) erstellt wird.

Ruft Benutzer A die SP auf und die dauert 1 Stunde und dazwischen rufen Benutzer B bis Z die SP auf, mit 'ner Laufzeit von 1 Sekunde, wobei der letzte Aufruf von B bis Z garantiert vor dem Aufruf von A beendet wird. Was ist jetzt die letzte ID? Die von A, weil zuletzt fertig oder die von dem Benutzter, die zuletzt gestartet wurde, unabhängig vom Zeitpunkt der Beendigung?

Oder insert into LastID (GeneratorID, DeineID) values (Gen_ID(GEN_LastID, 1),Result)

Ist das immernoch nicht so ganz das, was gewünscht wurde, dann kannst Du Dir auch am Beginn der SP per SQL den nächsten Wert des Generators in einer Variabel speichern und dann diesen Wert am Ende beim Insert in die Tabelle schreiben.

Sinngemäß, ohne irgendwie auf korrekte Syntax zu achten:
SQL-Code:
procedure GetNextID
  select (Gen_ID(GEN_LastID, 1) into v_GenLastID; -- Zeile ggfls. dahin verschieben, wo sie im Ablauf korrekt positioniert ist.

  a := ErzeugePrefix;
 
  if Bedingung then
    b := gen_id(MeinGenerator, 1);
  else
    b := '';

  c := ErzeugeSuffix;

  result := a+b+c;

  insert into LastID (GeneratorID, DeineID) values (v_GenLastID, Result);
end;
Da die Fachlichkeit nicht wirklich bekannt ist, eventuell auch in der Form:
SQL-Code:
procedure GetNextID
 
  a := ErzeugePrefix;
  c := ErzeugeSuffix;
 
  if Bedingung then
    select (Gen_ID(GEN_LastID, 1) into v_GenLastID; -- je nach Fachlichkeit
    b := gen_id(MeinGenerator, 1); -- ggfls. Zeilen vertauschen
    insert into LastID (GeneratorID, DeineID) values (v_GenLastID, a+b+c);
  else
    b := '';

  result := a+b+c;
end;

Geändert von Delphi.Narium ( 5. Aug 2022 um 15:38 Uhr) Grund: Schreibfehler
  Mit Zitat antworten Zitat
BlueStarHH

Registriert seit: 28. Mär 2005
Ort: Hamburg
850 Beiträge
 
Delphi 11 Alexandria
 
#16

AW: Letzte vergebene Nummer speichern

  Alt 5. Aug 2022, 15:48
Was heißt denn das?

Dieser wird aber von Zeit zu Zeit auch wieder auf 1 gesetzt.
Mit System, eher zufällig?
Entweder zum Monatswechsel oder nur zum Jahreswechsel. Ist nachträglich nur anhand der ID nicht erkennbar.

Zur Klarstellung: Die ID ist hier *keine* Datensatz-ID. Sondern eine Art Seriennumer für einen Gegenstand. Wenn es sich um eine Datensatz ID handeln würde, würde ich einfach einen Generator nehmen, die nie zurücksetzen und fertig. Das ist hier aber nicht das Thema.

Was ist jetzt die letzte ID. Die, die beim letzten Aufruf der SP (durch wen auch immer) generiert wird, oder die, die beim letzten Beenden der SP (durch wen auch immer) erstellt wird.
Nichts von beiden. Ich verweise nochmal auf meinen Post #8 und #12 und verdeutliche es nochmals.

Die ID mit dem größten Generatorwert *direkt* nach dem Abfrage des Generators ist die letzte ID für mich. Dieser wird aber von Zeit zu Zeit auch wieder auf 1 gesetzt. Daher kann ich den nicht einfach so abfragen. Wann die SP aufgerufen oder beendet wird ist dafür nicht maßgeblich.
Wenn der Generator nicht benutzt wird, ist es prinipiell egal ob die letzte ID durch die zuletzt aufgerufene SP oder die zuletzt beendete SP festgelegt wird.

Geändert von BlueStarHH ( 5. Aug 2022 um 15:52 Uhr)
  Mit Zitat antworten Zitat
BlueStarHH

Registriert seit: 28. Mär 2005
Ort: Hamburg
850 Beiträge
 
Delphi 11 Alexandria
 
#17

AW: Letzte vergebene Nummer speichern

  Alt 5. Aug 2022, 15:55
Ich habe ein ähnliches Problem auch gehabt.
Meine Lösung im groben:
Vor dem Aufruf der SP (die die Nummer erzeugt) wird ein Datensatz in einer Sperrtabelle eingefügt.
Diese Tabelle hat einen eindeutigen Index auf einen Feld.
Dadurch ist ein doppeltes Einfügen nicht möglich. z.B. 'GETBUCHNR'
Nach dem Insert in die Sperrtabelle wird die Nummer per SP erzeugt, wobei diese prüft ob auch die Sperre gesetzt ist.
Danach wird der Datensatz mit der Nummer entweder eingefügt oder die Nummer gesetzet. Nach dem Commit wird die Sperre entfernt.

Die Funktion, die die Sperre anlegt, ist so aufgebaut, dass sie es bis zu eimen definieten Timeout wiederholt mit kurzem Sleep versucht, den Datensatz einzufügen.
Super, das hört sich gut an. Ist eine Art Critical Section. Damit ist dann sichergestellt, dass die SP als ganzes oder gar nicht abgearbeitet wird. Ich überlege gerade noch, ob es bei dieser Implementation Nachteile gibt.
  Mit Zitat antworten Zitat
Delphi.Narium

Registriert seit: 27. Nov 2017
2.508 Beiträge
 
Delphi 7 Professional
 
#18

AW: Letzte vergebene Nummer speichern

  Alt 5. Aug 2022, 16:51
Wir haben hier ein grundlegendes Missverständnis.

Die von mir eingeführte ID hat nichts, aber auch garnichts mit der von Dir erstellten ID zu tun.

Die GeneratorID dient ausschließlich zur Kennzeichnung der letzten DeineID in der Tabelle LastID. Sie ist absolut losgelöst vom Rest des Universums zu sehen. Sie sagt nur, wirklich nur: Das ist der zuletzt eingefügte Datensatz.
Selbst wenn die von Dir für die Fachlichkeit erstellte ID minütlich bei 1 anfangen würde, zeigte die GeneratorID trotzdem immer auf die zuletzt erstellte DeineID.

Wir müssen hier strikt zwischen GeneratorID und DeineID unterscheiden. Fachlich gibt es hier keinerlei Zusammenhang. Es ist also problemlos möglich, dass sowohl die GeneratorID 1, die GeneratorID 12345 und die GeneratorID 5126811 alle auf DeineID 1 verweisen.

Flapsig formuliert: Mit DeineID kannst Du machen was Du willst, (beliebiges Zeitinterval) zurücksetzen, neuen Algorithmus entwickeln, ...

Die Reihenfolge ergibt sich immer durch die GeneratorID, das ist nix weiter als ein AutoInc, bei dem der nächste Satz halt den nächsten Wert in 'ner definierten Reihenfolge bekommt. Darüber kannst Du dann (sofern die Daten in der Tabelle immer erhalten bleiben) feststellen, in welcher Reihenfolge für DeineID die 1 vergeben wurde. Wann wurde die 1 zum ersten Mal vergeben, wann zum 2. Mal, wann zum x-mal.

Tabelle LastID:
GeneratorIDDeineID
0001XM07-001-Z
0002XM07-002-A
0003XM07-003-X
0004XM07-U
0005XM07-A
0006XM08-001-X
0007XM08-002-B
0008XM09-001-B
0009A444-5
... 
1001XM07-001-Z
... 
2002XM07-002-A
... 
3003XM07-003-X
... 
4004XM07-U
... 
5005XM07-A
... 
6006XM08-001-X
... 
8007XM09-001-B
8008XM08-002-B
8009A444-5
Wenn's verständlicher wird, nenn' die GeneratorID einfach Reihenfolge
SQL-Code:
create Table LastID
(
  Reihenfolge BIGINT not null,
  DeineID Integer not null -- oder welcher Type das auch immer sein mag.
);
CREATE GENERATOR GEN_Reihenfolge;
SET GENERATOR GEN_Reihenfolge TO 0;

-- in der SP an geeigneter Stelle:
insert into LastID (Reihenfolge, DeineID) values (Gen_ID(GEN_Reihenfolge, 1), DeineID);

-- zur Abfrage der neuesten DeineID
select first 1 DeineID from LastID order by Reihenfolge desc;

-- zur Ansicht der Reihenfolge der DeineID-Vergabe:
select Reihenfolge, DeineID from LastId order by Reihenfolge.

-- soll nachgeschaut werden, ob es eine DeineID mehrfach gibt:
select Count(*) as Anzahl, DeineID from LastID group by DeineID having Count(*) > 1 order by DeineID

-- soll für eine bestimmte DeineID, die mehrfach vorhanden ist, die Reihenfolge der Vergabe angezeigt werden?
select Reihenfolge, DeineID from LastID where DeineID = 'WertDerGesuchtenDeineID';
Ist die pure Anzeige der Reihenfolge noch nicht ausreichend, dann spendiere der Tabelle noch eine TimeStamp-Spalte und befülle diese jeweils mit.
SQL-Code:
create Table LastID
(
  Reihenfolge BIGINT not null,
  DeineID Integer not null, -- oder welcher Type das auch immer sein mag.
  ZeitStempel TimeStamp
);
CREATE GENERATOR GEN_Reihenfolge;
SET GENERATOR GEN_Reihenfolge TO 0;

-- in der SP an geeigneter Stelle:
insert into LastID (Reihenfolge, DeineID, ZeitStempel) values (Gen_ID(GEN_Reihenfolge, 1), DeineID, Current_TimeStamp);

-- Abfrage von DeineID in zeitlicher Abfolge:
select Reihenfolge, DeineID, Zeitstempel from LastID order by ZeitStempel, Reihenfolge
-- sollten innerhalb eines ZeitTempels mehrere DeineIDs eingefügt werden, so kannst Du damit auch bei denen noch die Reihenfolge der Erstellung nachvollziehen.
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#19

AW: Letzte vergebene Nummer speichern

  Alt 5. Aug 2022, 18:51
Ein asynchrones Statement, in welchem nur die Nummer generiert wird.
https://www.firebirdfaq.org/faq16/
Wie dort, nur eben auf die eigene Datenbank. (die Werte für die Connection kann man von der aktuellen Connecion auslesen/nehmen)

Das ist dann unabhängig von Transaktionen und der Dauer des Executes.


Wir nutzen sowas für ein Log in der DB (Tabelle) ... bei einer Exception würde ja die Transaction zurückgesetzt und das Log würde verschwinden ... so aber nicht, da in einer unabhängigen Transaktion gespeichert wurde.
Bzw. was Ähnliches auch für Tabellen, welche über alle Entwicklungsdatenbanken geshared werden. (wie eine globale Tabelle, obwohl eigentlich Jeder seine Eigene hätte)
$2B or not $2B

Geändert von himitsu ( 5. Aug 2022 um 18:54 Uhr)
  Mit Zitat antworten Zitat
BlueStarHH

Registriert seit: 28. Mär 2005
Ort: Hamburg
850 Beiträge
 
Delphi 11 Alexandria
 
#20

AW: Letzte vergebene Nummer speichern

  Alt 5. Aug 2022, 19:06
Wir haben hier ein grundlegendes Missverständnis.
Ich habe schon verstanden, wie Dein Vorschlag gemeint ist. Dein Generator habe ich kein einziges mal erwähnt. Ich meinte nur meinen Generator, der bei der ID-Erstellung benutzt wird.

Das Problem liegt hier:

-- in der SP an geeigneter Stelle:
insert into LastID (Reihenfolge, DeineID) values (Gen_ID(GEN_Reihenfolge, 1), DeineID);
Kurzfassung: Es gibt keine geeignete Stelle, wo ich diesen Code einfügen kann. Da direkt vor dem Aufruf von diesem "insert into" das "insert into" von einem anderen Benutzer aufgerufen werden kann. Ich verdeutliche es mal:

Die SP könnte so aussehen:

Code:
SP
begin
  DeineID := ErzeugeMeineID;
  insert into LastID (Reihenfolge, DeineID) values (Gen_ID(GEN_Reihenfolge, 1), DeineID);
end;
Das könnte so ablaufen, wenn 2 Benuzer die SP starten:

Benutzer A startet die SP:
Benutzer A: DeineID := ErzeugeMeineID; // DeineID = XY-1

Die SP von A läuft noch. Bevor die SP von A zum "insert into" kommt, startet B die SP und diese wird vollständig beendet:
Benutzer B: DeineID := ErzeugeMeineID; // DeineID = XY-2
Benutzer B: insert into LastID (Reihenfolge, DeineID) values (Gen_ID(GEN_Reihenfolge, 1), XY-2); //XY-2 steht in LastID als letzte ID (Reihenfolge=1)

Nun kommt die SP von A erst zum insert into:
Benutzer A: insert into LastID (Reihenfolge, DeineID) values (Gen_ID(GEN_Reihenfolge, 1), XY-1); //XY-1 steht in LastID als letzte ID (Reihenfolge=2). Stimmt nicht!

Am Ende sieht die Tabelle LastID so aus:
Code:
Reihenfolge DeineID
1           XY-2
2           XY-1
So müsste sie aber aussehen:
Code:
Reihenfolge DeineID
1           XY-1
2           XY-2

Geändert von BlueStarHH ( 5. Aug 2022 um 19:49 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 2 von 4     12 34      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:06 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz