Tom: Automatisches Aufnummerieren in einer Abfrage

Guten Tag Forumsgemeinde

Ich stehe vor folgendem Problem:
Wie kann in SQL ich mittels eines Update-Statements der Wert eines Feldes in der Abrage aufnummerieren, beginnend mit einem vorgegebenen Startwert.

Die (beispielhafte) Tabelle sieht so aus:

BestID  ArtNr   RechNr       LieferDatum
1002    45860   70058        27.02.00
1003    31120   70059        29.02.00
1004    68167                01.03.00
...
1026    58996                23.03.00
1027    72156                26.03.00
1028    48902                28.03.00
1029    64537                02.04.00
...

Für Betsllungen die im Monat Februar geliefert wurden, sind schon Rechnungsnummern generiert.
Jetzt sollen die Bestellungen für den Monat März abgerechnet werden, d.h. eine (eindeutige) Rechnungsnummer generiert werden.

Die Abfrage für die zu verändernden Datensätze sieht so aus:
SELECT BestID, ArtNr, RechNr, LieferDatum
FROM tblBestellungen
WHERE RechNr = null AND LieferDatum < 01.04.00      <- Vorausgesetzt, dass ich hier mit richtigen Datumsfeldern arbeite.

Der Startwert für die Aufnummerierung der Rechnungsnummer gewinne ich mit folgendem Statement:
SELECT Max(RechNr) + 1
FROM tblBestellungen

Nun, wie kriege ich die Rechnungsnummer nun mit einem Update-Statement aufnummeriert?

So klappt es auf jeden Fall nicht:
UPDATE tblBestellungen
SET RechNr = ( SELECT Max(RechNr)+1 FROM tblBestellungen )
WHERE RechNr = null AND LieferDatum < 4/1/00

Hat jemand einen Tip oder Ratschlag?

Wichtig ist, dass dieses Aufnummerierung in einer Transaktion erfolgt, damit bei einem Fehlschlag ein vollständiger Rollback vorgenommen werden kann.
Aus diesem Grund scheitert auch das Abarbeiten der Abfrage in einer Schleife, da dort bei einem Fehler nur die Änderung am letzten Datensatz zurückgenommen werden kann, jedoch nicht die komplette Aufnummerierung der Rechnungsnummern.

Danke schon im voraus.

Grüsse
Tom

  1. Wichtig ist, dass dieses Aufnummerierung in einer Transaktion erfolgt, damit bei einem Fehlschlag ein vollständiger Rollback vorgenommen werden kann.
    Aus diesem Grund scheitert auch das Abarbeiten der Abfrage in einer Schleife, da dort bei einem Fehler nur die Änderung am letzten Datensatz zurückgenommen werden kann, jedoch nicht die komplette Aufnummerierung der Rechnungsnummern.

    Hi Tom,

    ich habe auch keine Idee, wie man das einfach nur auf der SQL Ebene lösen könnte, aber zum Glück kann man mit Scripten so ziemlich alles hinbiegen.

    Ich hätte da 2 Lösungsansätze zu diesem Problem.

    1. Wie wäre es, wenn du z.B. ein Script programmierst, das die Daten nicht in einer Schleife, sondern Zeile für Zeile abarbeitet? Du könntest dann den letzten Update auf dem Bildschirm ausgeben und quasi auf Knopfdruck warten, befor das Script automatisch das nächste Update vornimmt. So kannst du jeden Schritt genau beobachten und falls was schiefgeht drückst du eben nicht den Knopf, sondern machst dein Rollback.

    2. Ich würde gar nicht so viel Zeit und Umstand in die Fehlerprofilxe investieren, da es meiner Meinung nach mehr Aufwand ist, als einen Fehler zu korrigieren. Insbesondere deswegen, weil Scripte Roboter sind und stur nach Muster vorgehen, also geht meistens alles schief oder gar nichts schief. Ausnahmen sind sehr selten.
    Du kannst dein SQL Query mit einem Script zusammenbasteln und durch eine Schleife jagen und danach mit wenig Mühe einen Report ausgeben, ob alles geklappt hat, oder eben die Zeilen oder IDs ausgeben von Fehlgeschlagenen Queries...oder was sich eben am besten anbietet, das ist von Fall zu Fall verschieden.

    Gruß
    Cruz

    1. Hallo Cruz

      Danke erst einmal für die prompte Antwort

      1. Wie wäre es, wenn du z.B. ein Script programmierst, das die Daten nicht in einer Schleife, sondern Zeile für Zeile abarbeitet? Du könntest dann den letzten Update auf dem Bildschirm ausgeben und quasi auf Knopfdruck warten, befor das Script automatisch das nächste Update vornimmt. So kannst du jeden Schritt genau beobachten und falls was schiefgeht drückst du eben nicht den Knopf, sondern machst dein Rollback.

      Geht leider nicht, da die Anwendung von DAUs bedient werden muss. Wenn bei denen ein Fehler auftritt, so soll ordnungsgemäss eine Fehlermeldung erscheinen, aber gleichzeitig auch wieder der Ursprungs-Zustand hergesetllt werden. Das Problem liegt in der Tatsache, dass nach einem Abruch das Feld 'RechNr' teilweise belegt ist und somit sind diese Datensätze in der weiderholten Bearbeitung ausgeschlossen.
      Zudem ist dies ein Job über mehrere hundert Datensätze, der ca. wöchentlich erfolgen sollte. Weiter sind Rechnungen und Rechnungsdaten etwas Sensibles für ein Unternehemn (bzw. deren Erfolg) ;-)

      1. Ich würde gar nicht so viel Zeit und Umstand in die Fehlerprofilxe investieren, da es meiner Meinung nach mehr Aufwand ist, als einen Fehler zu korrigieren. Insbesondere deswegen, weil Scripte Roboter sind und stur nach Muster vorgehen, also geht meistens alles schief oder gar nichts schief. Ausnahmen sind sehr selten.
        Du kannst dein SQL Query mit einem Script zusammenbasteln und durch eine Schleife jagen und danach mit wenig Mühe einen Report ausgeben, ob alles geklappt hat, oder eben die Zeilen oder IDs ausgeben von Fehlgeschlagenen Queries...oder was sich eben am besten anbietet, das ist von Fall zu Fall verschieden.

      Soweit bin ich auch schon. Trotzdem beschleichen mich noch erhebliche Sicherheitsbedenken.

      Danke trotzdem.
      Hoffentlich fällt jemand anderem noch eine Idee ein :-)

      Grüsse
      Tom

      1. Ok noch eine Idee...wie wäre es, wenn du mit dem Script gleichzeitig noch eine Undo History generierst, das nichts weiter ist, als eine Text Datei mit SQL Statements, die die RechngNr = NULL setzen für alle in dieser Session bearbeiteten Zeilen. Wenn irgendwas schifgeht drückst du auf UNDO und alle diese Statements werden schön abgearbeitet und alles ist wieder wie vorher.

  2. Hi!

    Wenn die ID der Datensätze lückenlos aufnummeriert sind, könntest du die Rechnungsnummer doch aus der ID errechnen?

    Oder du machst es doch in einer Schleife und merkst dir welche Datensätze du verändert hast (eventuell ein zusätzliches Feld in der Table in dem du anmerkst, daß die Rechnungsnr dieses Feldes gerade erzeugt wurde)

    mfG
    BRAND

  3. Wie kann in SQL ich mittels eines Update-Statements der Wert eines Feldes in der Abrage aufnummerieren, beginnend mit einem vorgegebenen Startwert.

    Reden wir von SQL, oder von einer abgemagerten real existierenden Implementierung?

    Wichtig ist, dass dieses Aufnummerierung in einer Transaktion erfolgt, damit bei einem Fehlschlag ein vollständiger Rollback vorgenommen werden kann.
    Aus diesem Grund scheitert auch das Abarbeiten der Abfrage in einer Schleife, da dort bei einem Fehler nur die Änderung am letzten Datensatz zurückgenommen werden kann, jedoch nicht die komplette Aufnummerierung der Rechnungsnummern.

    Die Logik dieser Aussage erschließt sich mir nicht:

    • Transaktion öffnen
    • Schleife ausführen
    • COMMIT oder ROLLBACK, je nach Wunsch.

    Aber ich würde es in der Tat anders lösen.
    In meiner Oracle-Datenbank funktioniert folgendes:

    CREATE SEQUENCE seq
           INCREMENT BY 1
           START BY <startwert>
           ORDER;

    Für Startwert einmalig "max(rechnr)+1"
    aus Deiner Tabelle einsetzen.

    Nun kannst Du sagen:

    UPDATE tblBestellungen
           SET rechnr = seq.nextval
           WHERE rechnr IS NULL;

    und bekommst lauter verschiedene Sequenznummern.
    Diese sind auch dann verbraucht, wenn Du die Transaktion
    mit ROLLBACK abbrichst; man kann den Generator aber
    durch ALTER SEQUENCE auch manuell zurücksetzen.

    Eleganter fände ich allerdings die Methode, erst gar
    keine undefinierten Rechnungsnummern zuzulassen.
    Was hindert Dich eigentlich daran, schon beim Eintragen
    des Datensatzes abzufragen, ob eine Rechnungsnummer
    vorliegt, und dann sofort eine zu generieren?
    (Ebenfalls durch Zugriff auf einen solchen Generator.)

    Am wenigsten Fehler kann der DAU immer noch bei einem
    Vorgang machen, der gar nicht existiert ...

  4. Guten Tag

    Erst einmal Danke an Cruz, brand und Michael.

    Michael:

    Reden wir von SQL, oder von einer abgemagerten real existierenden Implementierung?

    Letztendlich muss ich die Lösung schon auf einer real existierenden Implementation umsetzen. ;-)
    Diese ist: Front-End in MS-Access, Zugriff über ODBC auf MS-SQL 7.0.

    Da nun Oracle <> MS-SQL (ja ich weiss, Oracle ist eine richtige DB ;-)) habe ich nun keine Möglichkeit, eine Sequenz zu erzeugen.
    Dies steht sogar explizit im Manual zu MS-SQL:
    <cite page="Data Types in Oracle and SQL Server">
       Sequences and the IDENTITY Property
       If your Oracle application currently uses sequences to generate sequential numeric values, it can be altered to take advantage of the SQL Server IDENTITY property. The primary difference between SQL Server and Oracle is that the IDENTITY property is actually part of the column, while a sequence is independent of any tables or columns.
    </cite>
    Da in der Tabelle aber nicht alle Datensätze automatisch eine Rechn-Nr. bekommen sollen, fällt der Einsatz der Identity-Eigenschaft flach. Schade :-(

    Brand:

    Wenn die ID der Datensätze lückenlos aufnummeriert sind, könntest du die Rechnungsnummer doch aus der ID errechnen?

    Geht nicht, da die IDs unabhängig von der Rechnungsstellung sind (ich habe Datensätze in der realen Tabelle, die keine Rechnung bekommen sollen).

    Brand:

    Oder du machst es doch in einer Schleife und merkst dir welche Datensätze du verändert hast (eventuell ein zusätzliches Feld in der Table in dem du anmerkst, daß die Rechnungsnr dieses Feldes gerade erzeugt wurde)

    Cruz:

    Ok noch eine Idee...wie wäre es, wenn du mit dem Script gleichzeitig noch eine Undo History generierst, das nichts weiter ist, als eine Text Datei mit SQL Statements, die die RechngNr = NULL setzen für alle in dieser Session bearbeiteten Zeilen. Wenn irgendwas schifgeht drückst du auf UNDO und alle diese Statements werden schön abgearbeitet und alles ist wieder wie vorher.

    Diese Vorschläge werde ich kombinieren.

    Es wird ein zusätzliches Feld 'RechCheck' geben, in dem ich während des Schleifendurchlaufs gleichzeitig mit dem Setzen der RechNr ein Wert setze.
    Ist der ganze Vorgang erfolgreich abgeschlossen, so werde ich mit einem Update-Statement das Feld 'RechCheck' auf Null setzten, andernfalls wird das Feld 'RechNr' und 'RechCheck' auf Null gesetzt, falls das Feld 'RechCheck' nicht Null war.

    Danke an alle Beteiligten für die Mitarbeit an einer Lösung.

    Grüsse
    Tom