IDENTITY-Poblem (SQL Server 2016)



  • asc schrieb:

    Falls man eine solche Durchnummerierung braucht, muss man diese selbst vornehmen und sicherstellen das die Vergabe nicht durch Transaktionen beeinflusst werden kann.

    Hast du für diese Vorgehensweise vielleicht weiteres Infomaterial?

    Mein Projekt habe ich in C# mit ADO.NET umgesetzt.



  • Benni07 schrieb:

    Hast du für diese Vorgehensweise vielleicht weiteres Infomaterial?

    Nein, nur Projekterfahrungen. Und mit etwas wie deinen Fall bin ich bei meinem ersten Projekt konfrontiert wurden. Unser damaliger Projektleiter wollte die Id ebenso für eine direkt fortlaufende Nummerierung verwenden, die aber nicht einmal durchgehend fortlaufend war sondern auch noch Jahres- und Datenbezogen in unterschiedlichen Nummernkreisen vergeben werden musste.

    In dem Fall konnten wir den PL davon abbringen, auch weil Teile des Nummernkreises abhängig vom Status des Datensatzes waren und bei der Erzeugung ggf. noch nicht bekannt waren (sofern der Anwender nicht die Rechte hatte alle nötigen Daten gleich mitzuliefern). Da wurde es eine eigene Spalte, die erst im Nachgang gefüllt wurde (Und wenn ich mich recht erinnere auch mit Hilfe einer Tabellensperre zum Zeitpunkt des Bestimmens und Setzens der Nummer - Ist aber nun auch gute 15 Jahre her).



  • Das Problem sollte doch aber häufiger auftreten. In der Buchführung zum Beispiel müssen Belege zwingend fortlaufend lückenlos nummeriert werden. Merkwürdig, dass es für diese Problemstellung keine sinnvolle Lösung gibt. 🙄



  • Benni07 schrieb:

    Das Problem sollte doch aber häufiger auftreten. In der Buchführung zum Beispiel müssen Belege zwingend fortlaufend lückenlos nummeriert werden. Merkwürdig, dass es für diese Problemstellung keine sinnvolle Lösung gibt. 🙄

    Und wo steht, das diese Nummerierung zwangsläufig in der Id-Spalte geschehen muss? Oder das diese Nummerierung von der Datenbank durchgeführt wird?

    Eine Variante die vermutlich funktionieren würde:

    1. Tabellensperre setzen
    2. Datensatz speichern, Nummer mit MAX(<Spalte>) + 1 setzen. (Bei mehr als einen Datensatz, z.B. zusammenhängende Tabellen, dies zwangsweise innerhalb einer Transaktion ausführen).
    3. Tabellensperre entfernen

    Nachteil: Nicht geeignet für eine Massendatenbearbeitung.



  • Marc-O schrieb:

    Dreadlocks

    🕶



  • Mal ganz etwas anderes: Ist die lückenlose Nummerierung bei Belegen wirklich verpflichtend? Bei Rechnungen scheint dies nämlich nicht der Fall zu sein.



  • Benni07 schrieb:

    Das Problem sollte doch aber häufiger auftreten. In der Buchführung zum Beispiel müssen Belege zwingend fortlaufend lückenlos nummeriert werden. Merkwürdig, dass es für diese Problemstellung keine sinnvolle Lösung gibt. 🙄

    Ja, merkwürdig 🙄
    Ist dir klar warum es dabei konzeptuell ein Problem gibt?
    Klar kann man es lösen, aber jede Lösung hat Nachteile die man sich nicht eintreten will wenn es nicht nötig ist. Und da man es eben doch nicht so häufig wie von dir angenommen braucht...



  • asc schrieb:

    Mal ganz etwas anderes: Ist die lückenlose Nummerierung bei Belegen wirklich verpflichtend? Bei Rechnungen scheint dies nämlich nicht der Fall zu sein.

    In der Buchhaltung ist lückenlose Nummerierung bei Belegen wirklich verpflichtend.



  • hustbaer schrieb:

    Benni07 schrieb:

    Das Problem sollte doch aber häufiger auftreten. In der Buchführung zum Beispiel müssen Belege zwingend fortlaufend lückenlos nummeriert werden. Merkwürdig, dass es für diese Problemstellung keine sinnvolle Lösung gibt. 🙄

    Ja, merkwürdig 🙄
    Ist dir klar warum es dabei konzeptuell ein Problem gibt?
    Klar kann man es lösen, aber jede Lösung hat Nachteile die man sich nicht eintreten will wenn es nicht nötig ist. Und da man es eben doch nicht so häufig wie von dir angenommen braucht...

    Welche konzeptuellen Probleme meinst du?



  • Muss die fortlaufende Nummerierung sofort gesetzt werden, oder reicht es wenn es zu einem Zeitpunkt X durchgeführt wird?

    Dann würde ich eine weitere nummerische Spalte die Null enthalten kann anlegen, und einen SELECT oder eine STP schreiben die alle Datensätze bei denen die Spalte NULL ist mit dem jeweiligen Maximum + 1 füllt.



  • @Benni07
    Naja...
    Sagen wir so ... es kommt drauf an wie man es betrachtet.

    Dinge wie Belege sind komplexe Objekte die aus mehreren Einzelteilen zusammengesetzt sind. Beim Anlegen kann daher an verschiedenen Stellen 'was schief gehen. (Du kannst nen Beleg nicht mit einem einzigen INSERT anlegen, der hat ja vermutlich noch Belegzeilen usw.)
    Dann muss man zurückrollen. Um sicherzustellen dass dabei keine Löcher durch das Freiwerden der bereits "vergebenen" Nummer entstehen, muss man die ganze Transaktion in der so ein Beleg angelegt wird (und die Nummer vergeben) mit Isolation Level "serializable" machen. Weil ja sonst eine zweite Transaktion gleichzeitig laufen könnte, in der dann die nächste Nummer vergeben wird. Und wenn das erlaubt ist, und die 1. Transaktion abbricht, dann entsteht eben das verbotene Loch. *

    Das, also die Transaktion "serializable" machen, will man meistens nicht. Weil es praktisch verhindert dass Transaktionen gleichzeitig laufen können. Bzw. gibt es sogar DBs die es nichmal können.

    Also muss man irgendwie rumtricksen. z.B. so wie es asc vorgeschlagen hat. Nur damit erlaubt man dann Belege die erstmal gar keine Nummer haben. Wobei man damit vermutlich noch leben kann. Man müsste halt definieren dass ein Beleg der keine Nummer hat einfach nicht existiert.



  • asc schrieb:

    Muss die fortlaufende Nummerierung sofort gesetzt werden, oder reicht es wenn es zu einem Zeitpunkt X durchgeführt wird?

    Dann würde ich eine weitere nummerische Spalte die Null enthalten kann anlegen, und einen SELECT oder eine STP schreiben die alle Datensätze bei denen die Spalte NULL ist mit dem jeweiligen Maximum + 1 füllt.

    Die fortlaufende Nummerierung soll unmittelbar bei der Erfassung eines neuen Dokuments erfolgen. Pro Werktag sind das zurzeit zwischen 2 bis max. 25 Einträge.



  • Benni07 schrieb:

    Pro Werktag sind das zurzeit zwischen 2 bis max. 25 Einträge.

    Das ist super überschaubar wenig. Dafür reicht es wenn du das Gesamte Anlegen des Belegs/... in eine Transaktion packst, die Nummer per "MAX(Spalte) + 1" vergibst und einen Unique-Index auf die Spalte setzt. Wenn du dabei als Isolation-Level zumindest "READ COMMITTED" verwendest (und mindestens das ist glaube ich bei jedem RDBMS Standard) können so nie Löcher entstehen.

    Was maximal passieren kann, ist dass das Anlegen eines Belegs schief geht. Wenn zwei solche Vorgänge gleichzeitig ausgeführt werden. Bei "READ COMMITTED" würden dann beide Versuchen die selbe Nummer für den neuen Beleg zu vergeben, aber dank des Unique Index auf die Spalte lässt das DBMS das nicht zu, und wirft dir eine der beiden Transaktionen mit nem Fehler zurück. Und dann machst du halt nen Retry.



  • Endlich mal was Brauchbares. Das probiere ich am Wochenende mal aus.

    Danke schön!

    hustbaer schrieb:

    Benni07 schrieb:

    Pro Werktag sind das zurzeit zwischen 2 bis max. 25 Einträge.

    Das ist super überschaubar wenig. Dafür reicht es wenn du das Gesamte Anlegen des Belegs/... in eine Transaktion packst, die Nummer per "MAX(Spalte) + 1" vergibst und einen Unique-Index auf die Spalte setzt. Wenn du dabei als Isolation-Level zumindest "READ COMMITTED" verwendest (und mindestens das ist glaube ich bei jedem RDBMS Standard) können so nie Löcher entstehen.

    Was maximal passieren kann, ist dass das Anlegen eines Belegs schief geht. Wenn zwei solche Vorgänge gleichzeitig ausgeführt werden. Bei "READ COMMITTED" würden dann beide Versuchen die selbe Nummer für den neuen Beleg zu vergeben, aber dank des Unique Index auf die Spalte lässt das DBMS das nicht zu, und wirft dir eine der beiden Transaktionen mit nem Fehler zurück. Und dann machst du halt nen Retry.



  • Alter Schwede, ich muss wohl besser auf Gross-/Kleinschreibung aufpassen. 😞



  • Sind vlt. Trigger im Spiel?

    Unabhängig davon muss die BelegNr NICHT lückenlos sein. Die Stelle möchte ich sehen wo das steht. Da eine BelegNr auch gerne alphanumerisch sein darf, hat sich das mit "Lücken" bereits erledigt. Im Volksmund spricht man von "fortlaufend", soll heißen eindeutig. Kannst auch ne GUID nehmen, nur will die niemand auf dem Bildschirm sehen. 😉



  • "Fortlaufend" würde für mich auch mindestens noch implizieren dass spätere Rechnungen ne "höhere" Nummer haben (was ja auch bei Aplhanumerischen "Nummern" geht). Dann gingen GUIDs nicht.

    Zur eigentlichen Frage, nämlich ob die Nummern "lückenlos" sein müssen, kann ich aber auch nur Texte finden wo steht dass dies nicht erforderlich ist.



  • Benni07 schrieb:

    Endlich mal was Brauchbares. Das probiere ich am Wochenende mal aus.

    Ist es das wirklich und in jedem Fall?

    Nehmen wir einmal folgendes an (im Fall von Read Commited):
    Benutzer A beginnt Transaktion und Schreibt Beleg(e)
    Benutzer B beginnt Transaktion und Schreibt Beleg(e)
    Benutzer A [in Transaktion] ermittelt MAX(Belegnummer)
    Benutzer B [in Transaktion] ermittelt MAX(Belegnummer)
    Beide nutzer commiten...

    Entweder: bei Benutzer B gibt es einen Schreibfehler (Eindeutigkeitsverletzung, sofern in DB entsprechend gesetzt) oder du hast 2x die gleiche Belegnummer.

    Mit ersten kann man sicherlich leben, stollte den Fehlerfall aber zumindest im Hinterkopf halten.



  • Hab ich doch geschrieben. Also dass er nen Unique-Index braucht und dass dabei dann bei gleichzeitigem Eintragen das Anlegen von Belegen schief gehen kann. 😕

    Und ich bin sicher nicht beleidigt wenn du nen besseren Vorschlag hast (und Benny vermutlich auch nicht) 😉



  • hustbaer schrieb:

    Hab ich doch geschrieben. Also dass er nen Unique-Index braucht...

    Sorry, überlesen 😉

    Ganz davon abgesehen das ich auch nichts gefunden habe was eindeutig belegen würde das Belegnummern lückenlos sein müssen.


Anmelden zum Antworten