Ich habe folgende Konstellation:
Ein Kollege hat ein VB6 Programm entwicklet das sich zu einer Oracle-DB
per ODBC verbindet, Daten selektiert und in einer lokalen Access-DB
zwischenspeichert.
Jetzt haben wir Kunden die aber Citrix oder Terminalserver einsetzen.
Da konnten wir unser Produkt nicht verwenden, da in den lokalen
Tabellen kein Benutzer mit
gespeichert wurde.
Das ist jetzt meine Aufgabe.
Habe inzwischen in allen Access-Tabellen ein Feld "Benutzer"
hinzugefügt und mich darum gekümmert das es versorgt bzw.
berücksichtigt wird.
Das passt auch soweit.
Jetzt wollte ich Testen was passiert, wenn 2 Benutzer das Programm und
somit die selbe Access-DB gleichzeitig öffnen. Und dann gleichzeitig
eine Datenselektion aus Oracle nach Access durchführen.
Tja und da ist mein Problem. Hab schon rumgesucht finde aber nichts.
Das gleichzeitige öffnen klappt noch, aber wenn dann zwei Selektionen
gleichzeitig laufen kommt bei einer folgender Fehler:
-2147467259 - Aktualisierung nicht möglich, momentan gesperrt.
In dem Programm wird zu Beginn eine ADODB.CONNECTION mit der Acess-DB
hergestellt, die erst beim Programmende wieder geschlossen wird.
Set v_DbConn2 = New ADODB.Connection
v_DbConn2.Mode = adModeShareDenyNone
v_DbConn2.Provider = "Microsoft.Jet.OLEDB.4.0"
v_DbConn2.Properties("Data Source") = App.Path &
"\Database\IFSHR.mdb"
v_DbConn2.CursorLocation = adUseClient
v_DbConn2.Open
Hier dann die Verarbeitung:
Aus v_DbRset1 wird selektiert (v_dbconn1 = Oracle)
Und in der v_DbRset2 wird dann geschrieben.
Kann es eventuell an dem Delete liegen?
Set v_DbRset1 = New ADODB.Recordset
Set v_DbRset2 = New ADODB.Recordset
Set v_DbRset3 = New ADODB.Recordset
Set v_DbRset4 = New ADODB.Recordset
Set v_DbCmd2 = New ADODB.Command
Set v_DbCmd2.ActiveConnection = v_DbConn2
v_DbCmd2.CommandType = adCmdText
If v_DbTrSupp2 Then
v_DbConn2.BeginTrans
End If
v_DbCmd2.CommandText = "DELETE FROM Mitarbeiter_Basisdaten WHERE
Benutzer = '" _
+ v_UserName + "' AND Sachgebiet = 'PA' AND
Report_Id = 'PA1'" & _
" AND Lauf_Id = 1"
Set v_DbRset2 = v_DbCmd2.Execute
If v_DbTrSupp2 Then
v_DbConn2.CommitTrans
v_DbConn2.BeginTrans
End If
v_DbRset1.CursorLocation = adUseClient
v_DbRset3.CursorLocation = adUseClient
v_DbRset4.CursorLocation = adUseClient
v_DbConn1.BeginTrans
Call v_DbRset1.Open("SELECT a.company_id, a.emp_no, a.emp_name, " &
_
"a.entry_date_calc, " & _
"b.person_id, c.date_of_birth " & _
"FROM ifsapp.pi_emp_basic a, ifsapp.company_emp
b, " & _
"ifsapp.pers c " & _
"WHERE a.company_id = '" + v_CompanyId + "' " &
_
"AND a.company_id = b.company " & _
"AND a.emp_no = b.employee_id " & _
"AND b.person_id = c.person_id ", _
v_DbConn1, adOpenForwardOnly, adLockReadOnly,
adCmdText)
v_DlgProgressMax = v_DbRset1.RecordCount
Call v_DbRset2.Open("Mitarbeiter_Basisdaten", v_DbConn2,
adOpenDynamic, adLockOptimistic, adCmdTable)
Über Hilfe wäre ich sehr dankbar.
Gruß Michael
bin zwar kein Access-Profi (da kenn ich einen anderen hier :-)), deshalb nur
soweit:
> Set v_DbConn2 = New ADODB.Connection
> v_DbConn2.Mode = adModeShareDenyNone
Wieso dieser Mode? Du öffnest die DB damit exklusiv nur für dich selber.
Für Access im Hinblick auf Multiuserbetrieb ist die Seite von Peter fast
schon Pflicht:
www.gssg.de. Dort unter VB classic ADO DemoMU 2002.
Das ist ein Paradebeispiel dafür.
--
Gruß, Norbert
[mailto:nsch...@freenet.de]
> Ich habe folgende Konstellation:
>
> Ein Kollege hat ein VB6 Programm entwicklet das sich zu einer
> Oracle-DB per ODBC verbindet, Daten selektiert und in einer
> lokalen Access-DB zwischenspeichert.
>
> Jetzt haben wir Kunden die aber Citrix oder Terminalserver
> einsetzen. Da konnten wir unser Produkt nicht verwenden,
> da in den lokalen Tabellen
Wer oder was sind diese "lokalen Tabellen"?
> kein Benutzer mit gespeichert wurde.
>
> Das ist jetzt meine Aufgabe.
> Habe inzwischen in allen Access-Tabellen ein Feld "Benutzer"
> hinzugefügt und mich darum gekümmert das es versorgt bzw.
> berücksichtigt wird.
> Das passt auch soweit.
>
> Jetzt wollte ich Testen was passiert, wenn 2 Benutzer das Programm
> und somit die selbe Access-DB gleichzeitig öffnen.
Solange das Öffnen der *.mdb im geteilten Modus (adShareDenyNone)
geschieht passiert erst mal noch gar nichts.
> Und dann gleichzeitig eine Datenselektion aus Oracle
> nach Access durchführen.
Dann kommt es zu einem typischen Mehrbenutzerkonflikt, der einen Fehler
auslöst, den Dein Programmcode erkennen und entsprechend auflösen muss.
> Tja und da ist mein Problem. Hab schon rumgesucht finde aber nichts.
Was suchst Du denn?
Wie man eine Fehlererkennung/Fehlerbehandlung schreibt?
Oder wonach suchst Du sonst?
> Das gleichzeitige öffnen klappt noch, aber wenn dann zwei
> Selektionen gleichzeitig laufen kommt bei einer folgender Fehler:
>
> -2147467259 - Aktualisierung nicht möglich, momentan gesperrt.
Ja logisch. Was sollte denn sonst passieren?
Eine Access.mdb ist kein aktiver Datenbankserver wie Oracle oder
SQL-Server, sondern eine simple Datei, die einfach nur irgendwo
herumleigt, selbst nichts tun kann und nur darauf wartet, gelesen oder
beschrieben zu werden. Die eigentliche DB-Funktionalität liegt hier auf
der Clientseite. Falls Du mit ADO und dem Jet-Provider arbeitest,
bedeutet das, dass die Jet-Engine beim jeweiligen Client läuft und die
natürlich im Voraus nicht wissen kann ob und wann andere Clients
ebenfalls auf die *.mdb zugreifen. Die Jet-Engine kann somit Abfragen
(SQL-Statements) verschiedener Clients nicht in einer Warteschlange
sammeln und dann in geordneter Reihenfolge abarbeiten. Hier bleibt also
nur die Wahl, z.B. einen Schreibzugriff erst mal zu versuchen. Die
Fehlererkennung in Deinem Programmcode sagt Dir dann, ob es gut
gegangen ist oder nicht. Falls nicht, sollte die anschliessende
Fehler-/Konfliktbehandlung nach einer zufällig langen Zeit im Bereich
von z.B. 150 bis 500 Millisekunden einen erneuten Versuch starten.
Üblicherweise macht man so was in einer Schleife, die max. 3 bis 5 mal
durchlaufen wird. Hat es beim 5. Versuch immer noch nicht geklappt,
kann man sicher sein, dass ein dauerhaftes, nicht per Code behebbares
Problem vorliegt und man gibt dem Benutzer eine passende Fehlermeldung
aus.
> In dem Programm wird zu Beginn eine ADODB.CONNECTION mit der
> Acess-DB hergestellt, die erst beim Programmende wieder geschlossen
> wird.
> Set v_DbConn2 = New ADODB.Connection
> v_DbConn2.Mode = adModeShareDenyNone
> v_DbConn2.Provider = "Microsoft.Jet.OLEDB.4.0"
> v_DbConn2.Properties("Data Source") = App.Path &
> "\Database\IFSHR.mdb"
> v_DbConn2.CursorLocation = adUseClient
> v_DbConn2.Open
Das passt so weit. Allerdings sollte man diesem Code schon auch eine
vernüftige Fehlererkennung und Fehlerbehandlung spendieren. Es könnte
ja sein, dass das Öffnen der Connection aus irgendeinem Grunde nicht
gelingt und wenn das Programm dann einfach sang- und klanglos abstürzt
ist das sicher nicht sehr benutzerfreundlich.
> Hier dann die Verarbeitung:
>
> Aus v_DbRset1 wird selektiert (v_dbconn1 = Oracle)
> Und in der v_DbRset2 wird dann geschrieben.
> Kann es eventuell an dem Delete liegen?
Nein, es liegt an keinem Delete, sondern einfach an der fehlenden
Fehler-/Konflikterkennung mit nachfolgender Fehler-/Konfliktbehandlung.
Schau Dir mal
www.gssg.de -> Visual Basic -> VBclassic
-> Datenbank -> ADO DemoMU 2002
an. Das ist ein Beispiel für einen Mehrbenutzerbetrieb mit einer
Access.mdb.
Gruß aus St.Georgen
Peter Götz
www.gssg.de (mit VB-Tipps u. Beispielprogrammen)
> bin zwar kein Access-Profi (da kenn ich einen anderen hier :-)),
deshalb nur
> soweit:
>
> > Set v_DbConn2 = New ADODB.Connection
> > v_DbConn2.Mode = adModeShareDenyNone
>
> Wieso dieser Mode?
Weil er die DB so öffnen will, dass auch andere Benutzer zur gleichen
Zeit zugreifen können.
> Du öffnest die DB damit exklusiv nur für dich selber.
Nö, nicht wirklich.
Um exklusiv mit der *.mdb zu arbeiten, müsste die Connection mit
adModeShareExclusive geöffnet werden.
sags ja, hätt mich raushalten sollen :-(.
> > > v_DbConn2.Mode = adModeShareDenyNone
> Weil er die DB so öffnen will, dass auch andere Benutzer zur gleichen
> Zeit zugreifen können.
>
> > Du öffnest die DB damit exklusiv nur für dich selber.
>
> Nö, nicht wirklich.
> Um exklusiv mit der *.mdb zu arbeiten, müsste die Connection mit
> adModeShareExclusive geöffnet werden.
Hmmm .. dann scheint mir der Unterschied zwischen
adModeShareDenyNone("Verhindert das Öffnen einer Verbindung mit beliebigen
Berechtigungen durch andere Personen") und adModeShareExclusive ("Verhindert
das Öffnen einer Verbindung durch andere Personen.") nicht klar zu sein.
Muss ich mir die ADO-Hilfe noch mal genauer durchlesen. Hab ich dann falsch
interpretiert.
Danke für die Aufklärung.
@Michael: Tut mir leid. Vergiss das dann besser.
> Hmmm .. dann scheint mir der Unterschied zwischen
> adModeShareDenyNone("Verhindert das Öffnen einer Verbindung
> mit beliebigen Berechtigungen durch andere Personen")
> und adModeShareExclusive ("Verhindert das Öffnen einer
> Verbindung durch andere Personen.") nicht klar zu sein.
Die Doku zu ConnectModeEnum sagt:
adModeShareDenyNone
16
Allows others to open a connection with any permissions.
Neither read nor write access can be denied to others.
Die Betonung liegt hier auf "Allows" und
"with any permissions" sagt, dass die anderen Benutzer
Lesen, Schreiben und Löschen dürfen.
adModeShareExclusive
12
Prevents others from opening a connection.
Wogegen hier das "Prevents" der interessante Teil der Aussage ist.
Erst mal vielen Dank für die Antwort Peter.
ich versuche mal deine Fragen zu beantworten:
>> einsetzen. Da konnten wir unser Produkt nicht verwenden,
>> da in den lokalen Tabellen
>> kein Benutzer mit gespeichert wurde.
>Wer oder was sind diese "lokalen Tabellen"?
Was willst du genau wissen? Sind Tabellen in denen Personaldaten wie
z.b. Abrechnungsdaten o.ä. gespeichert werden. Aufbau einer Tabelle
nach meiner Änderung ist z.b.:
Benutzer (Index)
Firma (Index)
Periode (JJJJMM) (Index)
Personalnummer (Index)
Lohnart (Index)
Betrag
Ich habe in unserer Applikation dafür gesorgt das sich ein Benutzer
nur einmal an der Applikation anmelden kann. Also kann es nicht
vorkommen das ein Benutzer zweimal angemeldet ist.
Das Programm sammelt dann Daten von unserer Oracle-DB und speichert die
Lokal zwischen eben z.b. in dieser Tabelle.
Aus dieser Tabelle wird dann ein Auswertungswürfel erzeugt. usw.
>Dann kommt es zu einem typischen Mehrbenutzerkonflikt, der einen Fehler
>auslöst, den Dein Programmcode erkennen und entsprechend auflösen muss.
Wieso kommt es beim selektieren aus Oracle und speichern der Daten in
Access zu einem Mehrbenutzerkonflikt?
Angenommen ich selektiere einen Datensatz aus Oracle und schreibe Ihn
als Benutzer "Müller" in die Access-DB und ein anderer selektiert
einen Datensatz (von mir aus den selben) aus Oracle und schreibt ihn
aber als Benutzer "Maier" in die selbe Access-DB.
Dann kann es doch für mein Verständnis nicht zu einem
Mehrbenutzerkonflikt kommen.
Außer Access unterstützt dieses Verhalten nicht, was ich mir aber
nicht vorstellen kann.
Habe mir die Applikation ADO DemoMU (Multiuser) von deiner HP
runtergeladen und da geht es doch auch, das 2 mal das Programm
gestartet ist und man in die selbe DB aus jeder Anwendung einen
Datensatz eintragen kann.
>Was suchst Du denn?
>Wie man eine Fehlererkennung/Fehlerbehandlung schreibt?
>Oder wonach suchst Du sonst?
Naja warum der Fehler kommt. Weil der Index doch eindeutig ist?
Die Fehlerbehandlung ist mir klar und ist auch per on error abgefangen.
Gruß Michael
> Ich habe in unserer Applikation dafür gesorgt das sich ein Benutzer
> nur einmal an der Applikation anmelden kann. Also kann es nicht
> vorkommen das ein Benutzer zweimal angemeldet ist.
Hast Du auch daran gedacht, dass jedes Connectionobjekt wie ein eigener
Benutzer behandelt wird. Wenn Du also innerhalb Deiner Anwendung
mehrere (z.B.) Connectionobjekte zu Deiner DB öffnest, dann sind das im
Ergebnis 3 verschiedene Benutzer. Jedes dieser Connectionobjekte
verwaltet auch einen eigenen Datenpuffer. Die Inhalte dieser
Datenpuffer sind ohne weiteres Zutun nicht jederzeit synchron.
> Das Programm sammelt dann Daten von unserer Oracle-DB und speichert
> die Lokal zwischen eben z.b. in dieser Tabelle.
> Aus dieser Tabelle wird dann ein Auswertungswürfel erzeugt. usw.
> >Dann kommt es zu einem typischen Mehrbenutzerkonflikt, der einen
> > Fehler auslöst, den Dein Programmcode erkennen und entsprechend
> > auflösen muss.
> Wieso kommt es beim selektieren aus Oracle und speichern der Daten
> in Access zu einem Mehrbenutzerkonflikt?
Es kommt dann zu einem Mehrbenutzerkonflikt, wenn ein anderer Benutzer
zeitgleich auf den selben Datensatz oder die selbe Seite (je nach dem
ob Du PageLocking oder RowLocking für Deine *.mdb eingestellt hast) in
der *.mdb zugreift.
> Angenommen ich selektiere einen Datensatz aus Oracle und schreibe
> Ihn als Benutzer "Müller" in die Access-DB und ein anderer
selektiert
> einen Datensatz (von mir aus den selben) aus Oracle und schreibt ihn
> aber als Benutzer "Maier" in die selbe Access-DB.
> Dann kann es doch für mein Verständnis nicht zu einem
> Mehrbenutzerkonflikt kommen.
Wenn sowohl Müller als auch Maier zur selben Zeit versuchen in die
*.mdb zu schreiben, dann kommt es zu einem Mehrbenutzerkonflikt. Eine
Datei kann nicht gleichzeitig von zwei verschiedenen Benutzern geändert
werden.
> Außer Access unterstützt dieses Verhalten nicht,
Du arbeitest doch nicht mit Access, sondern mit ADO.
Im übrigen ist Access eben nicht als Mehrbenutzeranwendung konzipiert
und löst evtl. auftretende Mehrbenutzer keineswegs automatisch.
> was ich mir aber
> nicht vorstellen kann.
Was ist daran so schwer vorstellbar?
Hast Du schon mal versucht eine einfache Textdatei von zwei oder drei
Benutzern aus wirklich gleichzeitig zu ändern?
Eine *.mdb ist kein aktives Programm wie ein SQL-Server oder Oracle,
welches eingehende SQL-Statements in einer Warteschlange ablegen und
von dort dann geordnet abarbeiten kann. Eine *.mdb ist eine simple
Datei wie jede andere Datei auch. Die Jet-Engine, also die eigentliche
Datenbankfunktionalität läuft bei der Arbeit mit einer *.mdb nicht auf
der Serverseite, sondern eben beim Client. Die Möglichkeit,
Datenanforderungen (SQL-Statements) in einer Warteschlange
zwischenzulagern und dann der Reihe nach abzuarbeiten gibt es für die
Jet-Engine nicht, da sie ja von den anderen Clients erst mal gar nichts
weiss.
> Habe mir die Applikation ADO DemoMU (Multiuser) von deiner HP
> runtergeladen und da geht es doch auch, das 2 mal das Programm
> gestartet ist und man in die selbe DB aus jeder Anwendung einen
> Datensatz eintragen kann.
Ja natürlich geht das, aber eben nur, weil es in dieser Anwendung eine
Konflikterkennung mit nachfolgender Konfliktlösung gibt. Das Prinzip
sieht so aus, dass beim Client A ein Fehler ausgelöst wird, wenn Client
A einen Datensatz ändern will, aber zur selben Zeit Client B gerade
auch an diesem Datensatz oder an dieser Seite (s. RowLocking vs.
PageLocking) etwas ändert.
Der Code für die Fehler-/Konflikterkennung bei Client A schaut sich den
Fehler an und entscheidet, dass in diesem Falle eine kurze Zeit (z.B.
150 ... 300 Millisekunden) gewartet werden muss und der
Änderungsversuch dann erneut gestartet wird. In den meisten Fällen wird
es schon nach dieser ersten Wiederholung gelingen, den Datensatz ohne
erneutes Auslösen eines Fehlers zu speichern. Wenn nicht, dann wird
eben wieder einen kurzen Moment gewartet und erneut versucht zu ändern.
Bei ADO DemoMU 2002 gibt es dazu eine Schleife, die max. 5 mal
durchlaufen wird. Die Praxis hat gezeigt, dass es so gut wie nie
vorkommt, dass ein Speichern auch mit 5 Versuchen nicht gelingt. Wenn
doch, dann ist ein anderes, nicht behebbares Problem aufgetreten und
der Benutzer bekommt eine entsprechend aussagekräftige Fehlermeldung
gezeigt.
> > Was suchst Du denn?
> > Wie man eine Fehlererkennung/Fehlerbehandlung schreibt?
> > Oder wonach suchst Du sonst?
> Naja warum der Fehler kommt. Weil der Index doch eindeutig ist?
Das hat nichts oder nicht in jedem Fall was mit dem Index zu tun.
Je nachdem welche Art von Locking (RowLocking oder PageLocking) Du
gewählt hast, wird beim Zugriff nur ein Datensatz gesperrt oder
mindestens eine ganze Seite (entspricht 2kB). RowLocking, also
datensatzweises Sperren wird erst ab Format Access2k unterstützt, bei
früheren Versionen ist nur PageLocking möglich. Generell sollte man
sich aber eben auch klar machen, dass die *.mdb eben eine einfache
Datei ist, die man nicht so ohne weiteres von verschiedenen Seiten aus
gleichzeitig verändern kann. Das geht mit einer Textdatei auch nicht.
> Die Fehlerbehandlung ist mir klar und ist auch per on error
abgefangen.
Das Abfangen alleine tuts noch nicht.
Wenn Deine Fehlererkennung festgestellt hat, dass es sich um einen
Mehrbenutzerkonflikt handelt, muss entsprechend reagiert werden,
sprich, einen kurzen Moment warten und einen erneuten Schreibversuch
starten.
Für die Wartezeit zwischen den einzelnen Wiederholungen sollte man
nicht eine immer gleichbleibende Zeit sondern eine per Zufallsgenerator
(z.B. im Bereich 150 ... 300 Millisekunden) erzeugte wählen. Damit
verhindert man, dass sich zwei Clients gegenseitig "aufschaukeln" und
sich immer wieder in die Quere kommen.
Da es beim Zugriff via Jet-Engine auf eine *.mdb eben kein
Serverprogramm gibt, welches an zentraler Stelle alle Zugriffe auf die
eigentlichen Datenbankdateien koordiniert, musst Du Dich um eben dieses
"Koordinieren" in Deinem Programmcode selbst kümmern. Die Schwierigkeit
dabei ist eben nur, dass die auf den verschiedenen Clients laufenden
Jet-Engines nichts voneinander wissen und also nur nach der Methode Try
and Error arbeiten können. Wenns dann zum Error kommt, ist Deine
Fehler-/Konfliktbehandlungsroutine an der Reihe.
danke für deine schnelle Antwort:
Ich habe in dem Programm nur ein Connection-Objekt auf die Datenbank.
Aber lange rede kurzer Sinn:
Wenn ich dich also richtig verstanden habe ist folgendes nicht
möglich:
Access-DB: geöffnet von Müller und Maier
Verarbeitung wird gestartet zeitgleich von Müller und Maier.
Maier schreibt Sätze in Tabelle "Personaldaten"
und
Müller schreibt Sätze in die Tabelle "Personaldaten"
Die Fehlerabfrage mit einer Schleife würde mir an dieser Stelle nichts
bringen,
da ja nicht nur 1 Satz, sondern unter umständen tausende pro Lauf in
die Tabelle "Personaldaten" geschrieben werden. Und ich schwer einem
Benutzer dauernd anzeigen kann "Wird gerade verwendet => Gleich nochmal
probieren".
Das kann unter umständen 10 Minuten dauern bis alle sätze geschrieben
sind.
Wenn das mit einer *.mdb nicht möglich ist dann muss ich wohl auf
SQL-Server umstellen :-(
Da gibts doch diese kostenlose Version vom SQL-Server MSDB? Oder wie
heißt die nochmal?
Danke
Peter Götz schrieb:
> Hallo Peter,
>
> danke für deine schnelle Antwort:
>
> Ich habe in dem Programm nur ein Connection-Objekt auf die Datenbank.
>
> Aber lange rede kurzer Sinn:
>
> Wenn ich dich also richtig verstanden habe ist folgendes nicht
> möglich:
>
> Access-DB: geöffnet von Müller und Maier
> Verarbeitung wird gestartet zeitgleich von Müller und Maier.
Wieso eigentlich dieser Umweg über die MDB?
Als Quelle hast du ORACLE.
Bau einen View für Müller
und einen View für Maier.
werte diese Views aus.
Wenn dir der View zu langsam ist, dann baue einen Materialized View
und bleibe in Oracle!
>
> Maier schreibt Sätze in Tabelle "Personaldaten"
> und
> Müller schreibt Sätze in die Tabelle "Personaldaten"
>
> Die Fehlerabfrage mit einer Schleife würde mir an dieser Stelle nichts
> bringen,
> da ja nicht nur 1 Satz, sondern unter umständen tausende pro Lauf in
> die Tabelle "Personaldaten" geschrieben werden. Und ich schwer einem
> Benutzer dauernd anzeigen kann "Wird gerade verwendet => Gleich nochmal
> probieren".
> Das kann unter umständen 10 Minuten dauern bis alle sätze geschrieben
> sind.
>
> Wenn das mit einer *.mdb nicht möglich ist dann muss ich wohl auf
> SQL-Server umstellen :-(
Wieso, bleibe doch in Oracle oder nimm für jeden der beiden Anwender je 1
MDB. Wenn schon lokal, dann richtig!
Wie dir andere hier schon sagten Access/mdb ist nicht für Multiuser
konzipiert. Wenn man es dennoch wagt, dann muß man clientseitig
entsprechende Vorkehrungen treffen.
Grüße
Matthias
das wäre mir ja am liebsten das ich in Oracle bleiben könnte. Nur dazu
müsste ich das ganze Programm komplett umschreiben.
Ich habe dazu aber leider nur ein paar Tage zeit.
Das mit den verschiedenen *.mdb-dateien für jeden Benutzer ist die Idee.
Warum bin ich da nur nicht früher drauf gekommen.
Zwar schade das das andere nicht geht aber ist halt leider so.
Gruß Michael
<M.Spitz...@googlemail.com> schrieb
[...]
>Access-DB: geöffnet von Müller und Maier
>Verarbeitung wird gestartet zeitgleich von Müller und Maier.
>Maier schreibt Sätze in Tabelle "Personaldaten"
>und
>Müller schreibt Sätze in die Tabelle "Personaldaten"
>Die Fehlerabfrage mit einer Schleife würde mir an dieser Stelle nichts
>bringen,
>da ja nicht nur 1 Satz, sondern unter umständen tausende pro Lauf in
>die Tabelle "Personaldaten" geschrieben werden. Und ich schwer einem
>Benutzer dauernd anzeigen kann "Wird gerade verwendet => Gleich nochmal
>probieren".
>Das kann unter umständen 10 Minuten dauern bis alle sätze geschrieben
>sind.
laß Dich nicht verrückt machen! Folgende Testcode
mit DAO, 1 Button, 1 Textfeld auf einer Form:
Private Sub Command1_Click()
Dim wrkJet As Workspace
Dim db As Database
Dim s As Long, e As Long
Set wrkJet = CreateWorkspace("", "admin", "", dbUseJet)
Set db = wrkJet.OpenDatabase("D:\db.mdb", False)
On Error Resume Next
Do
db.Execute "Insert into tblTest (user) values('" & Text1.Text & "')"
If db.RecordsAffected = 1 Then
s = s + 1
Else
e = e + 1
End If
Me.Cls
Me.Print "Gute: " & s & " Schlechte: " & e
DoEvents
If e > 2147483647 Or s > 2147483647 Then Exit Do
Loop
db.Close
End Sub
Ich habe die Anwendung kompiliert, 6x gestartet und
zusammen laufen lassen. Alle Anwendungen schreiben brav
Datensätze. Bei 250MB Daten breche ich ab. Die Anwendungen
haben zwischen 62000 und 500000 Datensätze geschrieben
und dabei lediglich zwischen 7 und 12 Fehler produziert.
Interessanterweise hat die erste Anwendung das 10-fache
an Datensätze geschrieben wie alle anderen. Warum Windows
diese Anwendung besser gestellt hat, weiß ich allerdings nicht.
Also, was mit DAO geht sollte mit ADO auch gehen, oder?
Wenn nicht, schmeiß ADO weg und nimm DAO!
Gruß
W. Wolf
also ich hab jetzt mal mein Trace erweitert und siehe da:
Es liegt scheinbar an dem delete.
Werde jetzt mal hier ne schleife bauen die es da abfängt und dann nochmal
probieren.
> Wenn ich dich also richtig verstanden habe ist folgendes nicht
> möglich:
>
> Access-DB: geöffnet von Müller und Maier
> Verarbeitung wird gestartet zeitgleich von Müller und Maier.
Hier stellt sich die Frage, was genau ist "Verarbeitung"?
Einen Datensatz ändern mit z.B. einem UpdateCommand viele Datensätze
ändern?
> Maier schreibt Sätze in Tabelle "Personaldaten"
> und
> Müller schreibt Sätze in die Tabelle "Personaldaten"
>
> Die Fehlerabfrage mit einer Schleife würde mir an dieser Stelle
> nichts bringen, da ja nicht nur 1 Satz, sondern unter umständen
> tausende pro Lauf in die Tabelle "Personaldaten" geschrieben
> werden.
Was genau bedeutet "pro Lauf"?
Wenn Du mehrere tausend Datensätze mit einem einzigen UpdateCommand
oder auch z.B. in einer Schleife schreiben möchtest, dann brauchst Du
eben für die Ausführung dieses UpdateCommands eine entsprechende
Fehlerbehandlung. Mehrere Commands in einer Schleife solltest Du in
eine Transaktion verpacken und diese entsprechend fehlerüberwachen,
oder im einem Recordset das mit adLockBatchOptimistic geöffnet wurde
erst mal alle Änderungen ausführen und diese dann in einem Stück per
RS.UpdateBatch zur DB übertragen.
> Und ich schwer einem Benutzer dauernd anzeigen kann
> "Wird gerade verwendet => Gleich nochmal probieren".
Das "gleich nochmal probieren" sollst Du nicht Deinem Benutzer
anzeigen, sondern das soll Dein Programmcode selbst machen, wenn es wg.
eines Benutzerkonfliktes notwendig ist.
> Das kann unter umständen 10 Minuten dauern bis alle sätze
> geschrieben sind.
Deinen Code aus dem ersten Posting durchschaue ich nicht wirklich, da
er zum einen durch eine Vielzahl von Zeilenumbrüchen kaum noch lesbar
ist und zum anderen gibt es darin div. Recordsets deren Sinn und Zweck
nicht erkennbar ist und Code, welcher irgendwelche Datensätze schreibt
oder updatet sehe ich gar nicht.
Wenn Du ein Recordset mit vielen geänderten Datensätzen hast, deren
Update 10 Minuten dauert, dann solltet du Dein Recordset mit
adLockBatchOptimistic öffnen, erst mal alle Änderungen im RS vornehmen
und das ganz dann im Bulk per RS.UpdateBatch speichern. Die zughörige
Fehlerbehandlung zeigt Dir dann ob und welche Datensätze evtl. nicht
gespeichert werden konnten (RS.Filter = adFilterConflictingRecords)
Mit Deiner Codezeile
Call v_DbRset2.Open _
("Mitarbeiter_Basisdaten", _
v_DbConn2, _
adOpenDynamic, _
adLockOptimistic, _
adCmdTable)
Öffnest Du ein Recordset.
CursorType = adOpenDynamic ist bei einer Access.mdb nicht möglich und
warum adCmdTable und nicht adCmdText?
Was anschliessend mit diesem Recordset geschehen soll, ist nicht
erkennbar.
> Wenn das mit einer *.mdb nicht möglich ist dann
> muss ich wohl auf SQL-Server umstellen :-(
Soweit ich aus Deinem Code herauslesen konnte baust Du erst mal einen
Delete-Command
v_DbCmd2.CommandText = _
"DELETE FROM Mitarbeiter_Basisdaten " & _
"WHERE Benutzer = '" + v_UserName + "' & _
"AND Sachgebiet = 'PA' AND Report_Id = 'PA1'" & _
" AND Lauf_Id = 1"
Set v_DbRset2 = v_DbCmd2.Execute
Wozu diese Zuweisung an das Recordset v_DbRset2?
Was erwartest Du in diesem Recordset?
Dann öffnest Du ein Recordset v_DbRset1, das seine Daten wohl aus der
ORACLE-DB bezieht und anschliessend öffnest Du wieder ein Recordset
v_DbRset2 das via v_DbConn2 mit der Access.mdb verbunden ist.
Was geschieht nun weiter mit diesem Recordset v_DBRset2?
Auch Deine Abfolge von v_DbConn2.BeginTrans und v_DbConn2.CommitTrans
durchschaue ich nicht so ganz.
Eigentlich verstehe ich schon gar nicht, was du überhaupt mit dieser
Access.mdb erreichen willst.
> Da gibts doch diese kostenlose Version vom SQL-Server MSDB?
> Oder wie heißt die nochmal?
Das wäre die MSDE (SQL2k) oder die neuere für .net geeignete Version
SQL Server Express. Ich glaube aber nicht, dass das Dein Problem besser
lösen würde. Irgendwie scheint mir Dein ganzes Programmkonzept nicht
wirklich durchdacht, oder aber Du beschreibst es so unzureichend, dass
ich nicht verstehe, was dabei wirklich gemacht werden soll.
also ich versuche erst mal etwas auszuholen:
Das ganze VB6 Programm ist ein Art - Datawarehouse.
Es wurde vor ca. 5 Jahren von meinem damaligen Chef entwickelt.
99,9% des Codes sind von ihm. Habe ab und zu nur einige Sachen ergänzt bzw.
korrigiert.
Das Programm macht folgendes:
Es gibt eine Einstiegsmaske in der ich auswählen kann, was ich für eine
Auswertung fahren will.
z.B. Personalstatistik = wie viele Leute habe ich zu einem bestimmten Datum
angestellt
Man macht in einer Eingabemaske dann diverse Vorgaben (also z.B. welchen
Stichtag ich betrachten will, oder ob ich z.b. meine Personalnummern nach
speziellen vorgaben einschränken will, usw.)
Wenn der Anwender damit fertig ist drückt er auf ausführen:
Das Programm rennt dann in die Verarbeitung und macht da folgendes:
1. Löscht aus der lokalen MDB die Daten der letzten Auswertung. (DELETE)
2. Selektiert aus unserer Oracle Anwendung alle Daten laut den Vorgaben.
Also in der Form (SELECT * from Personalstamm where eintritt <= &Stichtag
and Austritt >= &Stichtag)
3. Dann werden noch zusätzliche Prüfungen durchgeführt (z.B. weitere
selektionskriterien abgeprüft).
4. ist eine Personalnummer als "Gut" erkannt worden wird dann ein Datensatz
in die lokale MDB geschrieben.
5. ist die Verarbeitung zu ende (EOF von Oracle) dann wird je nach Auswahl
des Anwenders mit den Daten eine Excel-Datei / ein Crystal Report oder ein
Datenwürfel erzeugt.
6. Jetzt ist die Verarbeitung (der Lauf) zu ende und der Benutzer kann jetzt
seine Liste drucken oder mit dem Datenwürfel rumspielen.
Ab hier geht es dann immer wieder von vorne los. Also der benutzer wählt
sich eine neue Liste aus. Macht vorgaben und startet die selektion usw.
Das war im groben der Ablauf.
Warum das mit auch welchen Optionen aufgerufen wird (recordset usw.) kann
ich nicht sagen und habe es auch nicht geprüft, da ich es ja nicht entwickelt
habe.
hier nochmal ein Versuch die jeweiligen Codepassagen einzufügen:
Also öffnen der DB:
n dem Programm wird zu Beginn eine ADODB.CONNECTION mit der Acess-DB
hergestellt, die erst beim Programmende wieder geschlossen wird.
Set v_DbConn2 = New ADODB.Connection
v_DbConn2.Mode = adModeShareDenyNone
v_DbConn2.Provider = "Microsoft.Jet.OLEDB.4.0"
v_DbConn2.Properties("Data Source") = App.Path & "\Database\IFSHR.mdb"
v_DbConn2.CursorLocation = adUseClient
v_DbConn2.Open
Dann das löschen der letzten Selektion und die neue Selektion aus Oracle:
Set v_DbRset1 = New ADODB.Recordset
Set v_DbRset2 = New ADODB.Recordset
Set v_DbRset3 = New ADODB.Recordset
Set v_DbRset4 = New ADODB.Recordset
Set v_DbCmd2 = New ADODB.Command
Set v_DbCmd2.ActiveConnection = v_DbConn2
v_DbCmd2.CommandType = adCmdText
If v_DbTrSupp2 Then
v_DbConn2.BeginTrans
End If
'hier werden die alten Ergebnisse der letzten Selektion gelöscht
v_DbCmd2.CommandText = "DELETE FROM Mitarbeiter_Basisdaten WHERE Benutzer =
'" _
+ v_UserName + "' AND Sachgebiet = 'PA' AND Report_Id = 'PA1'" & _
" AND Lauf_Id = 1"
Set v_DbRset2 = v_DbCmd2.Execute
If v_DbTrSupp2 Then
v_DbConn2.CommitTrans
v_DbConn2.BeginTrans
End If
v_DbRset1.CursorLocation = adUseClient
v_DbRset3.CursorLocation = adUseClient
v_DbRset4.CursorLocation = adUseClient
v_DbConn1.BeginTrans
'das ist die Selektion aus Oracle
Call v_DbRset1.Open("SELECT a.company_id, a.emp_no, a.emp_name, " & _
"a.entry_date_calc, " & _
"b.person_id, c.date_of_birth " & _
"FROM ifsapp.pi_emp_basic a, ifsapp.company_emp b, " & _
"ifsapp.pers c " & _
"WHERE a.company_id = '" + v_CompanyId + "' " & _
"AND a.company_id = b.company " & _
"AND a.emp_no = b.employee_id " & _
"AND b.person_id = c.person_id ", _
v_DbConn1, adOpenForwardOnly, adLockReadOnly, adCmdText)
v_DlgProgressMax = v_DbRset1.RecordCount
'hier wird der Recordset zum Schreiben geöffnet
Call v_DbRset2.Open("Mitarbeiter_Basisdaten", v_DbConn2,
adOpenDynamic, adLockOptimistic, adCmdTable)
'jetzt werden die Daten Aus Orcale geprüft
Do Until v_DbRset1.EOF
'prüfungen
Call Check_pa1
'wenn der Check erfolgreich ist dann wird die Personalnummer geschrieben
IF check_ok = true Then
v_DbRset2.AddNew
v_DbRset2.Fields("Benutzer") = v_UserName
v_DbRset2.Fields("Sachgebiet") = "PA"
v_DbRset2.Fields("Report_Id") = "PA1"
v_DbRset2.Fields("Lauf_Id") = 1
v_DbRset2.Fields("Lauf_Sub_Id") = i
v_DbRset2.Fields("Company_Id") = v_DbRset1.Fields("COMPANY_ID")
v_DbRset2.Fields("Personal_Id") = v_DbRset1.Fields("EMP_NO")
ENd if
LOOP
Hoffe jetzt ist es etwas besser leserlich bzw. verständlich
Jetzt scheint mein problem behoben zu sein.
Sieht also so aus als würde nur ein gleichzeitiges "Delete" command zu dem
Gesperrt abbruch führen.
sofern ich Deinen Ablauf verstanden habe, sehe ich nicht, wozu Du
überhaupt eine Access.mdb brauchst.
> Man macht in einer Eingabemaske dann diverse Vorgaben (also z.B.
welchen
> Stichtag ich betrachten will, oder ob ich z.b. meine Personalnummern
nach
> speziellen vorgaben einschränken will, usw.)
>
> Wenn der Anwender damit fertig ist drückt er auf ausführen:
Die nachfolgenden Abläufe sind client/benutzerspezifisch?
Es wäre also eine *.mdb mit einer einzigen Tabelle bei jedem Client
vorhanden?
>
> Das Programm rennt dann in die Verarbeitung und macht da folgendes:
>
> 1. Löscht aus der lokalen MDB die Daten der letzten Auswertung.
(DELETE)
Damit wäre die Tabelle in der *.mdb leer.
> 2. Selektiert aus unserer Oracle Anwendung alle Daten laut den
Vorgaben.
> Also in der Form (SELECT * from Personalstamm where eintritt <=
&Stichtag
> and Austritt >= &Stichtag)
> 3. Dann werden noch zusätzliche Prüfungen durchgeführt (z.B. weitere
> selektionskriterien abgeprüft).
> 4. ist eine Personalnummer als "Gut" erkannt worden wird dann ein
Datensatz
> in die lokale MDB geschrieben.
Warum in eine *.mdb.
Erst mal doch einfach nur in ein Recordset.
> 5. ist die Verarbeitung zu ende (EOF von Oracle) dann wird je nach
Auswahl
> des Anwenders mit den Daten eine Excel-Datei / ein Crystal Report
oder ein
> Datenwürfel erzeugt.
Das lässt sich direkt aus dem Recordset heraus machen.
> 6. Jetzt ist die Verarbeitung (der Lauf) zu ende und der Benutzer
kann jetzt
> seine Liste drucken oder mit dem Datenwürfel rumspielen.
Auch das geht direkt aus dem Recordset.
> Ab hier geht es dann immer wieder von vorne los. Also der benutzer
wählt
> sich eine neue Liste aus. Macht vorgaben und startet die selektion
usw.
Also das Recordset löschen, am einfachsten per Set RS = new
ADODB.Recordset und das nächste Spiel beginnt.
> Das war im groben der Ablauf.
Ich sehe erst mal noch keinen Grund für eine *.mdb und
Benutzerkonflikte kann es so auch nicht geben, da dieses Recordset ja
nur dem einen Benutzer/Clilent gehört.
Ein Speichern in einer *.mdb könnte ich mir bestenfalls dann
vorstellen, wenn die Datenmenge zu gross würde um komplett im Speicher
als RS gehalten zu werden. Das scheint aber wohl nicht der Fall zu
sein.
Sollen die Daten im RS aus irgendwelchen Gründen über das Programmende
hinaus verfügbar bleiben, würde es genügen das Recordset als simple
(benutzereigene) Datei per Recordset.Save zu speichern. Eine solche
Datei wäre in jedem Fall deutlich kleiner als eine komplette *.mdb.
> Warum das mit auch welchen Optionen aufgerufen wird (recordset usw.)
kann
> ich nicht sagen und habe es auch nicht geprüft,
Solltest Du aber mal.
ADO biegt sich solche unzulässigen Eigenschaftswerte in zulässige
zurecht, was aber nicht unbedingt immer zum gewünschten Ergebnis führen
muss.
ich habe der Einfachkeit halber zahlreiche zwischenschritte weggelassen, in
denen die selektierten Daten weiterbearbeitet werden.
Außerdem werden hier teilweise bis zu 1 Million Datensätze aus Oraacle
selektiert.
Diese selektierten Ergebnisse werden dann in der MDB gespeichert um nicht
für die selbe liste, oder gewisser Änderungen der Auswahlparamater nochmal
die eventl. Million Datensätze nochmal zu holen.
Du wolltest wissen wie der Ablauf ist und das hab ich dir gesagt.
Glaub mir es ist notwendig über eine Zwischenspeicherung zu gehen.
Ich werde mir mal die Aufrufe ansehen.
Außerdem werde ich jetzt das ganze dahingehend ändern das jeder Benutzer seine
eigene MDB hat.
Scheint mir sicherer, einfacher und schneller zu realisieren.
Gruß Michael