none
Anwendung auf Multiusersystem mit DB-Anbindung umstellen. RRS feed

  • Frage

  • Hallo,

    an alle.

    Erklärung:

    Ich habe ein Programm für einen Einzelplatz geschrieben, welches so weit ganz gut läuft.

    Das Programm greift auf eine Sql-Server Express Version, im Momment Sql Server 2005.

    Im Programm wird auf eine Tabelle zugegriffen, in der für das laufende Jahr und das Vorjahr eine laufende Nummer erstellt wird.

    Frage:

    Wie muß man vorgehen um eine Anwendung für mehrere User zu erstellen, die gleichzeitig auf die laufende Nummer zugreift.

    Wie muß man die Aktuallisierung von Daten in den anderen Tabellen bewerkstelligen.

    Gibt es dazu eventuell ein Anwendung die man sich anschauen kann.

    Was muß alles beachtet werden.

    Erst einmal Danke im Voraus.

    PS: Ich hoffe ich habe meine Fragen richtig gestellt und was braucht ihr für Infos noch.

     

    Dienstag, 16. November 2010 10:24

Antworten

  • Hallo Michael,

    Das Programm, dass ich umstellen möchte, läuft bereits bei
    uns in der Firma, aber als Einzelplatzversion.

    Nun wurde der Vorschlag unterbreitet, es als Mehrplatzversion
    umzugestalten. Die Aufgabenstellung ist also schon bekannt.

    Das Schreiben von Daten zum gleichen Sachverhalt wurde mir
    überlassen.

    Leider weis ich jetzt nur nicht,

    1. wie muß ich mit einer laufenden Nummer für die Benutzer umgehen,
    da Sie eine Neuaufnahme beenden oder auch abbrechen können,
    und damit die laufende aktuelle Nummer verbraucht oder wieder
    freigegeben wird.
    2. wie kann man regeln das die freigegebene Nummer wieder genutzt
    werden kann, trotz das schon andere Datensätz mit der nächsten
    laufenden Nummer von anderen Usern aufgenommen worden
    3. was muß ich machen, wenn zwei User zur gleichen Zeit einen
    Datensatz ändern. Muss ich als Programmierer vor Update prüfen
    ob der Datensatz schon geändert wurde oder übernimmt der
    SQl-Server bestimmte Aufgaben

    Mit "laufender Nummer" meinst Du vermutlich eine fortlaufende,
    eindeutige Nummer für jeden Datensatz in Deiner DB-Tabelle.
    Dafür sollte es also ein Feld mit z.B. dem Namen "ID" geben,
    welches am einfachsten als Primärschlüsselfeld, welches nur
    eindeutige Werte (keine Duplikate) zulässt, definiert ist.

    Mit dem SQL-Statement

        Select Max(ID) From Tabelle

    kannst Du die bisher höchste vergebene ID ermitteln. Beim
    Speichern eines neuen Datensatzes addierst Du dazu den
    Wert 1 und verwendest das Ergebnis als ID für den neuen
    Datensatz.
    Es gibt nun zwei Möglichkeiten:
        1. Die von Dir soeben errechnete ID wurde noch von keinem
        anderen Benutzer belegt, das Speichern des neuen Datensatzes
        wird also problemlos erfolgen.
        2. Die von Dir soeben errechnete ID wurde zwischenzeitlich von
        einem anderen Benutzer belegt, das Speichern des neuen Datensatzes
        wird einen Fehler auslösen (da die ID bereits belegt ist).
        Diesen Fehler kannst Du abfangen, Deine ID ein weiteres mal um
        1 erhöhen und den Datensatz erneut speichern. Du kannst Diesen
        Vorgang in eine Schleife packen, die z.B. max. 5 mal durchlaufen
        wird. Sollte nach dem 5 Versuch das Speichern des neuen Datensatzes
        immer noch nicht gelingen, gibst Du eine Fehlermeldung aus.

    Mit obigem SQL-Statement kannst Du lediglich die jeweils bisher
    höchste vergebene ID ermitteln.
    Möchtest Du vorhandene Lücken auffüllen, die z.B. durch das Löschen
    von Datensätzen entstanden sind dann hilft dieses SQL-Statement
    weiter:

        SELECT Min(ID) + 1 AS NextID From Tabelle
        WHERE (Tabelle.ID > (StartID - 1)) AND
        Not Exists (Select ID From Tabelle As Y Where (Y.ID  = Tabelle.ID +1))

    StartID in der ersten Where-Klausel bestimmt, welche neue ID Du
    zurückbekommst. Wenn Deine ID's aufsteigend ab 0 beginnen, bekommst
    Du mit StartID = 0 die nächste freie ID ab 0.
    Mit StartID = 100 würdest Du die nächste freie ID ab ID 100 bekommen.
    Übergibst Du als StartID die bisher höchste ID, dann bekommst Du
    eben die nächst höhere freie ID zurück. Bei einer noch leeren Tabelle
    gibt dieses Statement 0 Datensätze zurück, was Du in Deinem
    Programmcode erkennen kannst und entsprechend als neue ID
    die von Dir gewünschte erste ID vergeben kannst.

    Beim Ändern eines bereits bestehenden Datensatzes musst Du
    vorher, wie Peter Fleischer schon angemerkt hat, eine Strategie
    zur Konfliktlösung festgelegt haben.
    Wenn einfach nur der Datensatz des zuletzt speichernden Benutzers
    in der DB-Tabelle verbleiben soll, dann genügt ein SQL-Statement
    wie dieses:

        UPDATE Tabelle SET FeldX = NeuerWert WHERE ID = 20

    d.h.: die Where-Klausel beinhaltet nur das Feld ID (z.B. Primärschlüssel)

    Soll dagegen beim Speichern der Änderung erkannt werden, ob der
    Datensatz zwischenzeitlich von einem anderen Benutzer geändert
    worden ist, dann hilft ein SQL-Statement wie dieses weiter:

        UPDATE Tabelle SET FeldX = NeuerWert
        WHERE ID = 20 And
            Feld1 = org.Wert And
            Feld2 = org.Wert And
            FeldX = orgWert

    Feld1, Feld2, FeldX sind alle in der Tabelle enthaltenen Felder.
    Um zu erkennen, ob der Datensatz zwischenzeitlich geändert
    worden ist muss die Where-Klausel Deines Update-Statements
    also alle Felder (mit Originalwert) umfassen. Hat ein anderer
    Benutzer eines oder mehrere Felder des Datensatz zwischenzeitlich
    geändert, wird Dein Update-Command scheitern, weil wg. der
    Where-Klausel über alle Felder der Datensatz nicht mehr gefunden
    wird. Den hierbei ausgelösten Fehler kannst Du wieder abfangen
    und in Deinem Code entsprechend reagieren.
    Eine mögliche Reaktion wäre z.B. dem Benutzer den momentanen
    Zustand des Datensatzes zu zeigen und dann zu fragen, ob er
    seine Änderung Speichern will oder eben nicht.

    Gruß aus St.Georgen
    Peter Götz
    www.gssg.de (mit VB-Tipps u. Beispielprogrammen)

    Donnerstag, 18. November 2010 11:36
  • Hallo Micha,

    zunächst ist solltest Du Dich mit Transaktionen aus Sicht des SQL Servers
    und wie es mit  ADO.NET umgesetzt wird:

    Transaktionen (Datenbankmodul)

    Transaktionen und Parallelität (ADO.NET)

    (Nicht jedes Detail musst Du wissen, aber die Konzepte solltest Du verstanden haben).

    Die typische Umsetzung in ADO.NET setzt auf optimistische Aktualisierung
    (in der MSDN Dokumentation auch als vollständige Parallelität übersetzt).

    Das Konzept wird sowohl vom (Sql)CommandBuilder als auch von typisierten DataSets
    umgesetzt (und auch das Entity Framework nutzt es).
    Für Deine Anwenung bedeutet es nun, dass Du Ausnahmen behandeln mußt,
    die auftreten, wenn ein Datensatz bereits von einer anderen Stelle geändert wurde,
    siehe z. B. Exemplarische Vorgehensweise: Behandeln einer Parallelitätsausnahme

    Wobei ich Dir empfehlen würde, dies für einen Teil Deiner eigenen Datenbank umzusetzen
    (und dazu am besten eine Kopie der Datenbank verwenden, damit man nichts kaputt macht).

    Hinweis: Man kann es an der Stelle beliebig kompliziert machen. In vielen Fällen reicht aber
    eine einfache Behandlung. Denn so oft kommen Konkurrenbedingungen i. d. R. nicht vor,
    nur selten werden zwei Leute den gleichen Kunden oder Auftrag usw. bearbeiten.

    Was die Vergabe einer Nummer über eine Nummertabelle angeht,
    hatte ich vor Jahren einige Prozeduren in den Newsgroup gepostet:

    Zähler erhöhen.
    Eine Prozedur hat an der Stelle einige Vorteile, denn um es abzudichten sind
    einige Zusatzanforderungen wie zusätzliche Sperrhinweise zu erfüllen.

    Gruß Elmar

     

    Donnerstag, 18. November 2010 13:48
    Beantworter
  • Hi Michael,
    fortlaufende Nummern mit der Möglichkeit der Rückgabe nicht genutzter Nummern musst du explizit selbst implementieren. Meist werden fortlaufende Nummern im Zusammenhang mit weiteren Kriterien eingesetzt, wie Nummernkreise, Jahresscheiben usw., so dass fortlaufende Nummern immer nur für einen Bereich gelten und Autowerte dafür nicht nutzbar sind. Ich löse so etwas, indem ich mir die erste nicht genutzte Nummer hole. Das kann man mit einem verschachtelten SQL oder mit Suche per Code machen. Alternativ zu diesem Lösungsweg kann man auch eine Liste der zurückgegebenen Nummern führen. Eine solche Liste kann man auch für vergebene Nummern nutzen, um die nächste zu ermitteln, um bereits zum Zeitpunkt der Datenerfassung eine Belegnummer zu haben, obwohl noch keine Daten abgespeichert wurden. Es gibt da viele Lösungsmöglichkeiten, die anhand der in der Firma festgelegten Orgabläufe bestimmt werden muss. Ich farge da immer die betreffenden Sachbearbeiter, wie sie das bisher ohne PC gemacht haben.
     
    Für das gleichzeitige Speichern eines Datensatzes gibt es die Möglichkeit der Erkennung einer Konkurrenzbedingung. Wenn diese eintritt, ist entsprechend festgelegter Konfliktlösungsstrategie zu verfahren. Die einfachste Lösung ist, ohne Konflikterkennung zu arbeiten und den letzten speichern lassen.
     

    Viele Grü�?e
    Peter
    Donnerstag, 18. November 2010 14:36

Alle Antworten

  • Hi Michael,
    du solltest zuerst eine Konfliktlösungsstrategie festlegen.
     
    Am einfachsten ist es, wenn festgelegt wird �??Der Letzte gewinnt�?�. Dann brauchst Du keine Rücksicht zu nehmen, wenn jemand etwas ändern. Wichtig dabei ist aber das Kapseln in Transaktionen, wenn mehrere Tabellen von Aktualisierungen betroffen sind und andere Nutzer wegen Inkonsistenzen Probleme bekommen können.
     
    Wenn nicht der letzte gewinnen soll, dann kannst Du mit Erkennung von Konkurrenzfehlern arbeiten (wird über entsprechende SQL Anweisungen bzw. Einstellungen im CommandBuilder gelöst). Wenn also zwischenzeitlich jemand schneller mit �?nderungen war, wird ein Fehler wegen Verletzung der Konkurrenzbedingungen ausgelöst. Wie Du darauf reagierst, hängt von der festgelegten Konfliktlösungsstrategie ab. Man kann den zwischenzeitlich veränderten Datensatz neu laden und den Bediener die Eingabe wiederholen lassen. Man kann prüfen, was geändert wurde und ggf. nur einige Feldinhalte rückschreiben. Man kann auch den Originalwert neu laden, so dass beim nächsten Speichern kein Fehler mehr entsteht.
     
    Besonders anspruchsvoll sind softwareseitige Blockierungen, z.B. in Reservierungssystem.
     
    --
    Peter
     
     
     
     
     
     
    Dienstag, 16. November 2010 15:59
  • Hallo Peter,

    danke für deine Antwort.

    Mit der Konfliktlösungsstrategie habe ich leider keine Erfahrung.

    Gibt  es dazu manchmal ein Projekt, welches man sich anschauen kann.

    Oder eine Beschreibung, wie man vorgeht.

     

    Bin schon auf der Suche hab aber leider noch nicht so das richtige gefunden.

     

    Micha

     

     

    Dienstag, 16. November 2010 19:36
  • Hi Michael,
    die Konfliktlösungsstrategie hat mit Programmieren erst einmal nichts zu tun. Sie wird aus der Aufgabenstellung abgeleitet und ist mit dem Auftraggeber abzustimmen und durch ihn auch bestätigen zu lassen. Basis für die Konfliktlösungsstrategie sind die Orgabläufe beim Auftraggeber, die entweder schon bekannt sind oder mit der Aufgabenstellung neu zu gestalten sind. In der Konfliktlösungsstrategie wird beschrieben, wie zu verfahren ist, wenn unterschiedliche Daten zum  gleichen Sachverhalt in der Datenbank zu speichern sind. Erst, wenn das klar ist, kann man mit der Programmierung/Implementierung beginnen.
     

    Viele Grü�?e
    Peter
    Mittwoch, 17. November 2010 06:04

  • Hallo Peter,

    danke für die Antwort.

    Das Programm, dass ich umstellen möchte, läuft bereits bei uns in der Firma, aber als Einzelplatzversion.

    Nun wurde der Vorschlag unterbreitet, es als Mehrplatzversion umzugestalten. Die Aufgabenstellung ist also schon bekannt.

    Das Schreiben von Daten zum gleichen Sachverhalt wurde mir überlassen.

    Leider weis ich jetzt nur nicht,

    1. wie muß ich mit einer laufenden Nummer für die Benutzer umgehen, da Sie eine Neuaufnahme beenden oder auch abbrechen können, und damit die laufende aktuelle Nummer verbraucht oder wieder freigegeben wird.
    2. wie kann man regeln das die freigegebene Nummer wieder genutzt werden kann, trotz das schon andere Datensätz mit der nächsten laufenden Nummer von anderen Usern aufgenommen worden
    3. was muß ich machen, wenn zwei User zur gleichen Zeit einen Datensatz ändern. Muss ich als Programmierer vor Update prüfen ob der Datensatz schon geändert wurde oder übernimmt der SQl-Server bestimmte Aufgaben

      Habe schon etwas gegoogelt um eine Bescheibung für ein Mehrplatzsystem zu finden, doch leider im Moment ohne großen Erfolg.

    Achso, habe ich bei meiner Frage am Anfang vergessen, bin in Visual Basic nur ein Anfänger.

     

    Viele Grüße

    Micha

    Donnerstag, 18. November 2010 08:34
  • Hallo Michael,

    Das Programm, dass ich umstellen möchte, läuft bereits bei
    uns in der Firma, aber als Einzelplatzversion.

    Nun wurde der Vorschlag unterbreitet, es als Mehrplatzversion
    umzugestalten. Die Aufgabenstellung ist also schon bekannt.

    Das Schreiben von Daten zum gleichen Sachverhalt wurde mir
    überlassen.

    Leider weis ich jetzt nur nicht,

    1. wie muß ich mit einer laufenden Nummer für die Benutzer umgehen,
    da Sie eine Neuaufnahme beenden oder auch abbrechen können,
    und damit die laufende aktuelle Nummer verbraucht oder wieder
    freigegeben wird.
    2. wie kann man regeln das die freigegebene Nummer wieder genutzt
    werden kann, trotz das schon andere Datensätz mit der nächsten
    laufenden Nummer von anderen Usern aufgenommen worden
    3. was muß ich machen, wenn zwei User zur gleichen Zeit einen
    Datensatz ändern. Muss ich als Programmierer vor Update prüfen
    ob der Datensatz schon geändert wurde oder übernimmt der
    SQl-Server bestimmte Aufgaben

    Mit "laufender Nummer" meinst Du vermutlich eine fortlaufende,
    eindeutige Nummer für jeden Datensatz in Deiner DB-Tabelle.
    Dafür sollte es also ein Feld mit z.B. dem Namen "ID" geben,
    welches am einfachsten als Primärschlüsselfeld, welches nur
    eindeutige Werte (keine Duplikate) zulässt, definiert ist.

    Mit dem SQL-Statement

        Select Max(ID) From Tabelle

    kannst Du die bisher höchste vergebene ID ermitteln. Beim
    Speichern eines neuen Datensatzes addierst Du dazu den
    Wert 1 und verwendest das Ergebnis als ID für den neuen
    Datensatz.
    Es gibt nun zwei Möglichkeiten:
        1. Die von Dir soeben errechnete ID wurde noch von keinem
        anderen Benutzer belegt, das Speichern des neuen Datensatzes
        wird also problemlos erfolgen.
        2. Die von Dir soeben errechnete ID wurde zwischenzeitlich von
        einem anderen Benutzer belegt, das Speichern des neuen Datensatzes
        wird einen Fehler auslösen (da die ID bereits belegt ist).
        Diesen Fehler kannst Du abfangen, Deine ID ein weiteres mal um
        1 erhöhen und den Datensatz erneut speichern. Du kannst Diesen
        Vorgang in eine Schleife packen, die z.B. max. 5 mal durchlaufen
        wird. Sollte nach dem 5 Versuch das Speichern des neuen Datensatzes
        immer noch nicht gelingen, gibst Du eine Fehlermeldung aus.

    Mit obigem SQL-Statement kannst Du lediglich die jeweils bisher
    höchste vergebene ID ermitteln.
    Möchtest Du vorhandene Lücken auffüllen, die z.B. durch das Löschen
    von Datensätzen entstanden sind dann hilft dieses SQL-Statement
    weiter:

        SELECT Min(ID) + 1 AS NextID From Tabelle
        WHERE (Tabelle.ID > (StartID - 1)) AND
        Not Exists (Select ID From Tabelle As Y Where (Y.ID  = Tabelle.ID +1))

    StartID in der ersten Where-Klausel bestimmt, welche neue ID Du
    zurückbekommst. Wenn Deine ID's aufsteigend ab 0 beginnen, bekommst
    Du mit StartID = 0 die nächste freie ID ab 0.
    Mit StartID = 100 würdest Du die nächste freie ID ab ID 100 bekommen.
    Übergibst Du als StartID die bisher höchste ID, dann bekommst Du
    eben die nächst höhere freie ID zurück. Bei einer noch leeren Tabelle
    gibt dieses Statement 0 Datensätze zurück, was Du in Deinem
    Programmcode erkennen kannst und entsprechend als neue ID
    die von Dir gewünschte erste ID vergeben kannst.

    Beim Ändern eines bereits bestehenden Datensatzes musst Du
    vorher, wie Peter Fleischer schon angemerkt hat, eine Strategie
    zur Konfliktlösung festgelegt haben.
    Wenn einfach nur der Datensatz des zuletzt speichernden Benutzers
    in der DB-Tabelle verbleiben soll, dann genügt ein SQL-Statement
    wie dieses:

        UPDATE Tabelle SET FeldX = NeuerWert WHERE ID = 20

    d.h.: die Where-Klausel beinhaltet nur das Feld ID (z.B. Primärschlüssel)

    Soll dagegen beim Speichern der Änderung erkannt werden, ob der
    Datensatz zwischenzeitlich von einem anderen Benutzer geändert
    worden ist, dann hilft ein SQL-Statement wie dieses weiter:

        UPDATE Tabelle SET FeldX = NeuerWert
        WHERE ID = 20 And
            Feld1 = org.Wert And
            Feld2 = org.Wert And
            FeldX = orgWert

    Feld1, Feld2, FeldX sind alle in der Tabelle enthaltenen Felder.
    Um zu erkennen, ob der Datensatz zwischenzeitlich geändert
    worden ist muss die Where-Klausel Deines Update-Statements
    also alle Felder (mit Originalwert) umfassen. Hat ein anderer
    Benutzer eines oder mehrere Felder des Datensatz zwischenzeitlich
    geändert, wird Dein Update-Command scheitern, weil wg. der
    Where-Klausel über alle Felder der Datensatz nicht mehr gefunden
    wird. Den hierbei ausgelösten Fehler kannst Du wieder abfangen
    und in Deinem Code entsprechend reagieren.
    Eine mögliche Reaktion wäre z.B. dem Benutzer den momentanen
    Zustand des Datensatzes zu zeigen und dann zu fragen, ob er
    seine Änderung Speichern will oder eben nicht.

    Gruß aus St.Georgen
    Peter Götz
    www.gssg.de (mit VB-Tipps u. Beispielprogrammen)

    Donnerstag, 18. November 2010 11:36
  • Hallo Micha,

    zunächst ist solltest Du Dich mit Transaktionen aus Sicht des SQL Servers
    und wie es mit  ADO.NET umgesetzt wird:

    Transaktionen (Datenbankmodul)

    Transaktionen und Parallelität (ADO.NET)

    (Nicht jedes Detail musst Du wissen, aber die Konzepte solltest Du verstanden haben).

    Die typische Umsetzung in ADO.NET setzt auf optimistische Aktualisierung
    (in der MSDN Dokumentation auch als vollständige Parallelität übersetzt).

    Das Konzept wird sowohl vom (Sql)CommandBuilder als auch von typisierten DataSets
    umgesetzt (und auch das Entity Framework nutzt es).
    Für Deine Anwenung bedeutet es nun, dass Du Ausnahmen behandeln mußt,
    die auftreten, wenn ein Datensatz bereits von einer anderen Stelle geändert wurde,
    siehe z. B. Exemplarische Vorgehensweise: Behandeln einer Parallelitätsausnahme

    Wobei ich Dir empfehlen würde, dies für einen Teil Deiner eigenen Datenbank umzusetzen
    (und dazu am besten eine Kopie der Datenbank verwenden, damit man nichts kaputt macht).

    Hinweis: Man kann es an der Stelle beliebig kompliziert machen. In vielen Fällen reicht aber
    eine einfache Behandlung. Denn so oft kommen Konkurrenbedingungen i. d. R. nicht vor,
    nur selten werden zwei Leute den gleichen Kunden oder Auftrag usw. bearbeiten.

    Was die Vergabe einer Nummer über eine Nummertabelle angeht,
    hatte ich vor Jahren einige Prozeduren in den Newsgroup gepostet:

    Zähler erhöhen.
    Eine Prozedur hat an der Stelle einige Vorteile, denn um es abzudichten sind
    einige Zusatzanforderungen wie zusätzliche Sperrhinweise zu erfüllen.

    Gruß Elmar

     

    Donnerstag, 18. November 2010 13:48
    Beantworter
  • Hi Michael,
    fortlaufende Nummern mit der Möglichkeit der Rückgabe nicht genutzter Nummern musst du explizit selbst implementieren. Meist werden fortlaufende Nummern im Zusammenhang mit weiteren Kriterien eingesetzt, wie Nummernkreise, Jahresscheiben usw., so dass fortlaufende Nummern immer nur für einen Bereich gelten und Autowerte dafür nicht nutzbar sind. Ich löse so etwas, indem ich mir die erste nicht genutzte Nummer hole. Das kann man mit einem verschachtelten SQL oder mit Suche per Code machen. Alternativ zu diesem Lösungsweg kann man auch eine Liste der zurückgegebenen Nummern führen. Eine solche Liste kann man auch für vergebene Nummern nutzen, um die nächste zu ermitteln, um bereits zum Zeitpunkt der Datenerfassung eine Belegnummer zu haben, obwohl noch keine Daten abgespeichert wurden. Es gibt da viele Lösungsmöglichkeiten, die anhand der in der Firma festgelegten Orgabläufe bestimmt werden muss. Ich farge da immer die betreffenden Sachbearbeiter, wie sie das bisher ohne PC gemacht haben.
     
    Für das gleichzeitige Speichern eines Datensatzes gibt es die Möglichkeit der Erkennung einer Konkurrenzbedingung. Wenn diese eintritt, ist entsprechend festgelegter Konfliktlösungsstrategie zu verfahren. Die einfachste Lösung ist, ohne Konflikterkennung zu arbeiten und den letzten speichern lassen.
     

    Viele Grü�?e
    Peter
    Donnerstag, 18. November 2010 14:36
  • Vielen Dank an alle für die vielen Tipps.

    Werde mir erstmal alles ansehen.

    Sollte ich noch Fragen haben, würde ich mich noch einmal melden.

     

    Viele Grüße

     

    Micha

    Donnerstag, 18. November 2010 15:30