ich möchte ein per SELECT-Anweisung erzeugtes Recordset datensatzweise
in einen 2. Recordset umkopieren. An und für sich sind die Tabellen
identisch, es kann aber sein, dass irgendwann wieder was geändert
wird. Ich will datensatzweise kopieren, weil ein einzlnes Feld (die
Record ID) neu gesetzt werden soll. Soviel ich weiss, gibt es in Access
(in meinem Fall A2K mit DAO) dafür keinen Befehl. (Warum eigentlich
nicht?!!!)
Frage 1: Unter welchen Voraussetzungen darf ich annehmen, dass der
folgende Code dies richtig macht?
For x = 1 To (rec1.Fields.Count)
rec1.Fields(x).Value = rec2.Fields(x).Value
Next x
Ich vermute, dass sich die Reihenfolge der Spalten zwischen den
Tabellen ändern kann, also dass Field(3) plötzlich in Field(4)
kopiert werden muss!
Das würde dann bedeuten, dass man in der 2 Tabelle zuerst das richtige
Feld über die Property .Name identifizieren muss und erst dann
kopieren darf.
Bei vielen Feldern dürfte es sich aus Rechenzeitüberelgungen
verbieten diese Operation in der Schleife auszuführen. Die Optimierung
dazu würde wie folgt aussehen:
- Recordsets öffnen
- einmalig Abbildung der einzelnen Felder feststellen und in ein Array
speichern
- dann in der Schleife unter Berücksichtigung der im Array
gespeicherten Zuordnung die Nutzdaten umkopieren
Frage 2: Hat jemand dafür Code
Frage 3: Bin ich ganz falsch und alles geht anders viel einfacher?
Walter
> Frage 3: Bin ich ganz falsch und alles geht anders viel einfacher?
Ohne rs ginge es mit "SELECT INSERT" oder einem INSERT mit SELECT-Klausel.
Dabei alle Felder bis auf das ID-Feld aufnehmen. Ein Spaltentausch ginge
dabei auch leicht, mußt nur die Nennung der Spalten entsprechend ändern
(...die FeldTypen müssen aber schon passen)
Unter ADO:
Wenn es NUR um da kopieren geht, kannst Du den kompletten ADO-rs mittels
Zwischenspeicherung per propertyBag oder XML kopieren. Anschliessend die
gewünschten Felder ändern.
--
Viele Grüße
Dieter
Rückfragen bitte nur in die Newsgroup!
EDV-Kommunikation Strassner e.K.
68623 Lampertheim
Internet: www.strassner.biz
Walter Schütz wrote:
> Frage 1: Unter welchen Voraussetzungen darf ich annehmen, dass der
> folgende Code dies richtig macht?
>
> For x = 1 To (rec1.Fields.Count)
> rec1.Fields(x).Value = rec2.Fields(x).Value
> Next x
>
> Ich vermute, dass sich die Reihenfolge der Spalten zwischen den
> Tabellen ändern kann, also dass Field(3) plötzlich in Field(4)
> kopiert werden muss!
Dein Code wird nur einwandfrei funktionieren, solange die Anzahl der
Felder in beiden RS gleich ist. Wichtig ist also vorher die Anzahl der
Felder zu vergleichen.
z.B
If rec1.Fields.Count <> rec2.Fields.Count then
MsgBox "Falsche Anzahl Felder"
Exit Function
End If
>
> Das würde dann bedeuten, dass man in der 2 Tabelle zuerst das richtige
> Feld über die Property .Name identifizieren muss und erst dann
> kopieren darf.
Der Name des Feldes kann auch direkt als Key eingegeben werden.
Dim fld as Field
For each fld in rec2.Fields
rec1.Fields(fld.Name).value = fld.value
next fld
> Bei vielen Feldern dürfte es sich aus Rechenzeitüberelgungen
> verbieten diese Operation in der Schleife auszuführen. Die Optimierung
> dazu würde wie folgt aussehen:
> - Recordsets öffnen
> - einmalig Abbildung der einzelnen Felder feststellen und in ein Array
> speichern
> - dann in der Schleife unter Berücksichtigung der im Array
> gespeicherten Zuordnung die Nutzdaten umkopieren
>
> Frage 2: Hat jemand dafür Code
>
> Frage 3: Bin ich ganz falsch und alles geht anders viel einfacher?
> Walter
>
HTH
Martin Roling
Von dem was ich verstanden habe, scheint mir die Idee mit
For each fld in rec2.Fields
rec1.Fields(fld.Name).value = fld.value
next fld
schon recht elegant. Ich glaube das könnte hinhauen. Selbst wenn es
schlecht auscodiert ist, wäre das für den laufenden Auftrag kein
Problem, weil die Datenmenge klein ist. Weiss jeamnd genaueres über
die Effizienz?
Was ist ein 'PropertyBag'? Mein Help kennt das auf alle
Fälle nicht und aus anderen Diskussionsbeiträgen (über Search) bin
ich auch nicht schlau geworden. Ich habe den Eindruck, dass damit der
ganze Recordset und nicht nur der aktuelle Datensatz kopiert wird.
Gibts in A2K das PropertyBag überhaupt schon, oder ist das etwas
modernes? Wo kann ich mich darüber schlau machen?
Walter
>>Was ist ein 'PropertyBag'? Mein Help kennt das auf alle
>>Fälle nicht und aus anderen Diskussionsbeiträgen (über Search) bin
>>ich auch nicht schlau geworden. Ich habe den Eindruck, dass damit der
>>ganze Recordset und nicht nur der aktuelle Datensatz kopiert wird.
>>Gibts in A2K das PropertyBag überhaupt schon, oder ist das etwas
>>modernes? Wo kann ich mich darüber schlau machen?
Das Property-Objekt gibt es in VB. Mir schwant, dass du gar nicht - wie der
Name der NG evt. vermuten ließe - mit VB, sondern mit VBA arbeitest?
- microsoft.public.de.access
--
Gruß, Norbert
[mailto:nsch...@freenet.de]
> Was ist ein 'PropertyBag'? Mein Help kennt das auf alle
> Fälle nicht und aus anderen Diskussionsbeiträgen (über Search) bin
> ich auch nicht schlau geworden. Ich habe den Eindruck, dass damit der
> ganze Recordset und nicht nur der aktuelle Datensatz kopiert wird.
> Gibts in A2K das PropertyBag überhaupt schon, oder ist das etwas
> modernes? Wo kann ich mich darüber schlau machen?
"PropertyBag" kennt VB. In einem PropertyBag kannst Du objekte
"serialisieren", irgendwo speichern und wieder herstellen (wird zB. genutzt
um Eigenschaften von Objekten zu speichern (Entwurfszeit).
Die PropertyBag-Sache geht vermutlich aber nur ADO-rs, nicht mit DAO (DAO
kennt kein disconncted-rr, der erstellte rs könnte vermutlich nicht
verwendet werden, kannst ja mal testen).
Vom Prinzip her:
Public Function PBFromRS(ByRef rs As ADODB.Recordset, Optional ByRef
PropertyName As String = "rs")
' Wandle Recordset in ByteArray um
Dim PB As New PropertyBag
PB.WriteProperty PropertyName, rs
PBFromRS = PB.Contents
End Function
Public Function RsFromPB(ByRef ByteArray, Optional ByRef PropertyName As
String = "rs") As ADODB.Recordset
' Wandle BVyte-Array in Recordset zurück
Dim PB As New PropertyBag
PB.Contents = ByteArray
Set RsFromPB = PB.ReadProperty(PropertyName)
End Function
Obwohl die Umstellung auf ADO für dieses Projekt vermutlich zu
aufwendig ist, habe ich Dein Beispiel mal kurz in einem neuen Modul
compiliert. Bei der Zeile
Dim PB As New PropertyBag
erhalte ich die Message 'User-defined type not defined'. Ich nehme an,
das kommt daher, dass ich A2K und nicht VB benutze. Auch im VB-Help
findet sich ekein Hinweis auf PropertyBag (Sollte ev. doch mal auf eine
neuere Version updaten - wenn ich nur wüsste, ob meine Kunden in
Kombination mit A2K dann noch glücklich sind).
Eine Detailfrage noch: bei der ersten Funktion gibst Du im Header für
das Resultat kein AS an. Ist das Ergebnis dann vom Typ VARIANT?
Wäre die Anwendung so etwa richtig:
Dim ba as Variant
set rst1 = db.Openrecordset("SELECT ...")
rst1.MoveFirst
While Not rst1.eof
ba = PBFromRS (rst1)
rst2.AddNew
rst2 = RsFromPB(ba)
rst2!ID = myID
rst2.Update
rst1.MoveNext
Wend
...
Sorry für die hässliche Einrückung, da habe ich keinen Einfluss
drauf!
Walter
> Obwohl die Umstellung auf ADO für dieses Projekt vermutlich zu
> aufwendig ist, habe ich Dein Beispiel mal kurz in einem neuen Modul
> compiliert. Bei der Zeile
>
> Dim PB As New PropertyBag
>
> erhalte ich die Message 'User-defined type not defined'. Ich nehme an,
> das kommt daher, dass ich A2K und nicht VB benutze. Auch im VB-Help
> findet sich ekein Hinweis auf PropertyBag ...
Wie Peter schon vermutet hat... PropertyBag funktuiniert in VB6, nicht in
VBA
> ... (Sollte ev. doch mal auf eine
> neuere Version updaten - wenn ich nur wüsste, ob meine Kunden in
> Kombination mit A2K dann noch glücklich sind).
Neue Version würde heißen, VBA verlassen. Dann wäre aber VB.net eine
Überlegung wert....
> Eine Detailfrage noch: bei der ersten Funktion gibst Du im Header für
> das Resultat kein AS an. Ist das Ergebnis dann vom Typ VARIANT?
Eigentlich hat es den type "Byte-Array", Type "Byte" klappt nicht in diesem
Zusammenhang, deshalb "Variant"
> Wäre die Anwendung so etwa richtig:
> Dim ba as Variant
Nicht ganz, den der PropertyBag wirkt uf den kompletten Recordset, nicht auf
eine Zeile!
Es läßt sich zudem noch verkürzen. Du darfst auf den kopierten rs auch erst
zugreifen, wenn er aus dem PropertyBag geladen wurde. Hier die "optimierte
Form":
Dim rs1 As ADODB.Recordset
Dim rs2 As ADODB.Recordset
Dim PB As PropertyBag
'// defin+fill new rs1 //
Set PB = New PropertyBag
PB.WriteProperty "rs", rs1
Set rs2 = PB.ReadProperty("rs")
rs2.AddNew ' jetzt neue Datensätze dazunehmen, die alten aus rs1 sind
schon drin.
> rst2!ID = myID
Das wird nicht funktionieren, wenn es ein Autoinkrement-Feld ist. Diese
ID's vergibt bekanntlich die DB selbst. Also in diesem Falle leerlassen
> Sorry für die hässliche Einrückung, da habe ich keinen Einfluss
> drauf!
Irgendwas an deinem Newsreader ist aber schon verstellt...
> Wie Peter schon vermutet hat...
Hmm..... Danke Helga :-)))
> ich möchte ein per SELECT-Anweisung erzeugtes
> Recordset datensatzweise in einen 2. Recordset umkopieren.
Um welche Art von Recordset handelt es sich dabei?
ADODB.Recordset oder DAO.Recordset?
Mit welcher Programmiersprache arbeitest Du?
Ist es wirkliche VB oder handelt es sich um Access.VBA?
> An und für sich sind die Tabellen identisch, es kann
> aber sein, dass irgendwann wieder was geändert
> wird.
Wer oder was könnte geändert werden?
> Ich will datensatzweise kopieren, weil ein einzlnes Feld (die
> Record ID) neu gesetzt werden soll.
With NeuesRS
.Fields(0).Value = 12345
For i = 1 to .Fields.Count -1
.Fields(i).Value = AltesRS.Fields(i).Value
next i
End with
> Soviel ich weiss, gibt es in Access (in meinem Fall
> A2K mit DAO) dafür keinen Befehl.
Hmm, hört sich an, als würdest Du nicht mit VB sondern mit VBA
programmieren:
> (Warum eigentlich nicht?!!!)
Warum kann eine Kuh nicht fliegen?
> Frage 1: Unter welchen Voraussetzungen darf ich annehmen, dass der
> folgende Code dies richtig macht?
Unter gar keiner Voraussetzung.
> For x = 1 To (rec1.Fields.Count)
rec1.Fields.Count gibt die Anzahl der Felder in rec1 zurück.
Das erste Feld in einem Datensatz hat die Ordinalposition 0, das letzte hat
die Ordinalposition RS.Fields.Count - 1.
Dein Code wird also auf einen Fehler laufen, wenn die Schleifenvariable x
den Wert rec1.Fields.Count erreicht hat, da es ein solches Feld nicht mehr
gibt. Das letzte Feld in einem Recordset kannst Du mit
RS.Fields(RS.Fields.Count-1)
ansprechen.
> rec1.Fields(x).Value = rec2.Fields(x).Value
> Next x
>
> Ich vermute, dass sich die Reihenfolge der Spalten zwischen den
> Tabellen ändern kann, also dass Field(3) plötzlich in Field(4)
> kopiert werden muss!
Wenn sich nur die Reihenfolge (Ordinalposition) der Felder, aber nicht der
Name und Datentyp ändert, dann könnte Deine Schleife so aussehen:
RSNeu.Fields("ID").Value = RSAlt.Fields("ID".Value
For i = 1 to RSAlt.Fields.Count -1
RSNeu.Fields(RSAlt.Fields(i).Name).Value = RSAlt.Fields(i).Value
Next i
> Das würde dann bedeuten, dass man in der 2 Tabelle zuerst das
> richtige Feld über die Property .Name identifizieren muss und
> erst dann kopieren darf.
Siehe vorangehendes Beispiel. Du kannst ein Feld über seinen Feldnamen oder
über seine Ordinalposition ansprechen.
Die Ordinalposition der einzelnen Felder kannst Du aber schon mit der
Reihenfolge der Feldnamen in Deinem SELECT-Statement bestimmen, mit welchem
Du Dein Recordset erstellst.
> Bei vielen Feldern dürfte es sich aus Rechenzeitüberelgungen
> verbieten diese Operation in der Schleife auszuführen.
> Die Optimierung dazu würde wie folgt aussehen:
> - Recordsets öffnen
> - einmalig Abbildung der einzelnen Felder feststellen und in
> ein Array speichern
> - dann in der Schleife unter Berücksichtigung der im Array
> gespeicherten Zuordnung die Nutzdaten umkopieren
Du hast in jedem Fall zwei Recordsets. Das alte mit den zu kopierenden Daten
und das neue in welches die Daten kopiert werden sollen. Du müsstes bei
Deinem Vorschlag entweder mit RS.GetRows() die Daten aus dem alten RS in ein
VariantArray holen oder in einer Schleife Feld für Feld in ein vorher
entsprechend dimensioniertes Array kopieren. Im Anschluss daran müssten alle
Werte aus dem Array wieder in das neue Recordset kopiert werden. Das alles
zusammen wird sicher nicht schneller gehen also das direkte Kopieren von
einem RS zum anderen.
> Frage 2: Hat jemand dafür Code
>
> Frage 3: Bin ich ganz falsch und alles geht anders viel einfacher?
> Walter
Aus Deiner Beschreibung geht leider nicht hervor, welchen Zweck Dein
"Kopieren eines Datensatzes" wirklich haben soll. Vermutlich sollen Deine
Daten von einer Tabelle in eine andere kopiert werden. Sofern beide Tabellen
den gleichen Feldaufbau haben brauchst Du dazu überhaupt keine Recordsets
sondern kannst mit einem "INSERT INTO ..."-Statement eine beliebige Anzahl
von Datensätzen von einer in eine andere Tabelle kopieren.
Dim strSQL as String
strSQL = "INSERT INTO Zieltabelle " & _
"(Feldname1, Feldname2, FeldnameX) " & _
"SELECT Feldname1, Feldname2, FeldnameX " & _
"FROM Quelltabelle"
DeinDaoDBObjekt.Execute strSQL
Sollen nur bestimmte Datensätze kopiert werden musst Du obiges SQL-Statement
um eine entsprechende Where-Klausel erweitern.
Gruß aus St.Georgen
Peter Götz
www.gssg.de (mit VB-Tips u. Beispielprogrammen)
... och nicht der Rede Wert Willi ;-))
Sorry, hatte wohl zuvor ein Posting von Peter Fleischer gelesen...
Besten Dank für Deine ausführliche Antwort. VB.net steht fest auf
meinem Einkaufszettel, habe nur den Moment immer rausgeschoben wegen
der Fristen im Empower-Programm.
> Nicht ganz, den der PropertyBag wirkt auf den kompletten Recordset,
nicht auf eine Zeile!
Wenn das so ist, bin ich doch nicht sicher, ob man meine Fragenstellung
mittels Propertybag lösen kann. Ich möchte ja gerade jedem einzelnen
Datensatz die ID neu zuweisen (die ID ist nicht Autoinkrement, sondern
von Hand erzeugt. Ich habe nur im Codebeispiel die Inkrementierung
etwas übereifrig wegoptimiert um nur das Nötigste zu zeigen...).
> Irgendwas an deinem Newsreader ist aber schon verstellt...
... Ich lese und schreibe über Google (groups-beta.google.com) und das
Edit-Fenster entfernt Random die führenden Blanks und manchmal auch
das zweitletzte CrLf ...
Walter
> Wenn das so ist, bin ich doch nicht sicher, ob man meine Fragenstellung
> mittels Propertybag lösen kann. Ich möchte ja gerade jedem einzelnen
> Datensatz die ID neu zuweisen (die ID ist nicht Autoinkrement, sondern
> von Hand erzeugt. Ich habe nur im Codebeispiel die Inkrementierung
> etwas übereifrig wegoptimiert um nur das Nötigste zu zeigen...).
wichtiger wäre, ob DAO überhaupt beim Update in die DB mitspielt!
Die ID kannst Du ja in eine Do-While-movenextrecord-schleife einstellen.
> Wenn das so ist, bin ich doch nicht sicher, ob man meine
> Fragenstellung mittels Propertybag lösen kann. Ich möchte
> ja gerade jedem einzelnen Datensatz die ID neu zuweisen
> (die ID ist nicht Autoinkrement, sondern von Hand erzeugt.
> Ich habe nur im Codebeispiel die Inkrementierung etwas
> übereifrig wegoptimiert um nur das Nötigste zu zeigen...).
Was soll sich denn nun an Deinem ID-Feld ändern?
Ich denke mal, dass es sich insgesamt um eine relativ geringe Anzahl von
Datensätzen handelt, welche geändert werden sollen.
Wie vielen Datensätzen pro Minute möchtest Du kopieren und dabei einen neuen
Wert für ID vergeben?
In aktuellen Falle geht es um einige hundert Datensätze mit ca. 9-15
Feldern in 10 verschiedenen Tabellen. Die Datensätze sind an 5
verschiedenen Arbeitsplätzen erfasst worden und müssen für die
Auswertung wieder in eine gemeinsame Datenbank. Das hätte bedeutet,
dass ich ca. 50 Feldnamen einzeln hätte in den Code übernehmen (und
später unterhalten) müssen. Für den Datentransfer arbeite ich mit
Excel-Dateien die automatisch erzeugt worden sind und auch wieder
automatisch eingelesen werden.
Ich habe unterdessen den Code wie folgt zusammenschustert (noch
ungetestet) und nehme an, dass es so für meine Ansprüche (beim
aktuellen Problem) reichen wird:
Set rstAusw = db.OpenRecordset("SELECT tblDataAusw.* FROM
tblDataAusw;")
Set rstImp = db.OpenRecordset("SELECT tblDataImp.* FROM tblDataImp
ORDER BY & tblDataImp.ID;")
rstImp.MoveFirst
While Not rstImp.EOF
rstAusw.AddNew
For Each fld In rstImp.Fields
rstAusw.Fields(fld.Name).Value = fld.Value
Next fld
hiIndex = hiIndex + 1
rstAusw.Fields("ID").Value = hiIndex
rstAusw.Update
rstImp.MoveNext
Wend
rstAusw.Close
rstImp.Close
Je nach Tabelle werden weitere Felder auch als Referenzen auf andere
Datensätze verwendet.
Ich habe aber vor 3 Monaten auch schon 30'000 Datensätze mit je ca. 85
Feldern so um die 600 mal abgearbeitet. Damals habe ich alle 85 Felder
mit einzelnen Zuweisungen kopiert... (ich weiss, dass man mittels
Queries/INSERT INTO arbeiten kann. Das hat aber damals nicht gepasst,
weil einzelne Felder bearbeitet werden mussten). Schon damals habe ich
mich gefragt, wie man einen ganzen Datensatz aufs Mal kopieren kann.
Die Sache mit der PropertyBag hat mich eher verständnishalber für
zukünftige Problem interessiert (man gibt sich ja Mühe noch etwas
dazuzulernen...).
Besten Dank für Eure guten Ideen.
Walter
> In aktuellen Falle geht es um einige hundert Datensätze mit ca. 9-15
> Feldern in 10 verschiedenen Tabellen.
Also Peanuts, was die Datenmenge anbelangt.
Einige hundert Datensätze in einer Schleife Feld für Feld zu kopieren
beansprucht keine für den Benutzer unangenehm erkennbare Zeit.
> Die Datensätze sind an 5
> verschiedenen Arbeitsplätzen erfasst worden und müssen für die
> Auswertung wieder in eine gemeinsame Datenbank.
Kein wirkliches Problem, wenn alle diese Datenbanken die selbe
Tabellen/Feld-Struktur haben.
Hierzu wäre u.U. auch das Thema Replikation in der Doku zu Access sehr
empfehlenswert.
> Das hätte bedeutet,
> dass ich ca. 50 Feldnamen einzeln hätte in den Code
> übernehmen (und später unterhalten) müssen.
Warum 50 Feldnamen, wenn die Tabellen nur 9 ... 15 Felder haben?
> Für den Datentransfer arbeite ich mit Excel-Dateien
> die automatisch erzeugt worden sind und auch wieder
> automatisch eingelesen werden.
Auweia, das ist aber wohl nicht eine wirklich professionelle Lösung.
> Ich habe unterdessen den Code wie folgt zusammenschustert (noch
> ungetestet) und nehme an, dass es so für meine Ansprüche (beim
> aktuellen Problem) reichen wird:
> Set rstAusw = db.OpenRecordset("SELECT tblDataAusw.* FROM
> tblDataAusw;")
> Set rstImp = db.OpenRecordset("SELECT tblDataImp.* FROM tblDataImp
> ORDER BY & tblDataImp.ID;")
>
> rstImp.MoveFirst
> While Not rstImp.EOF
> rstAusw.AddNew
> For Each fld In rstImp.Fields
> rstAusw.Fields(fld.Name).Value = fld.Value
> Next fld
> hiIndex = hiIndex + 1
> rstAusw.Fields("ID").Value = hiIndex
> rstAusw.Update
> rstImp.MoveNext
> Wend
> rstAusw.Close
> rstImp.Close
Na ja, geht so. Könnte man aber schon noch einiges daran feilen.
In der For Each Schleife kopierst Du alle Felder, auch das ID-Feld und
anschliessend weist Du diesem Feld dann wieder einen neuen Wert zu. Es würde
genügen, alle Felder ausser ID zu kopieren und dann dem Feld ID den
gewünschten Wert zuzuweisen.
> Je nach Tabelle werden weitere Felder auch als Referenzen auf andere
> Datensätze verwendet.
>
>
> Ich habe aber vor 3 Monaten auch schon 30'000 Datensätze
> mit je ca. 85 Feldern so um die 600 mal abgearbeitet.
> Damals habe ich alle 85 Felder mit einzelnen Zuweisungen
> kopiert... (ich weiss, dass man mittels Queries/INSERT INTO
> arbeiten kann. Das hat aber damals nicht gepasst,
> weil einzelne Felder bearbeitet werden mussten).
> Schon damals habe ich mich gefragt, wie man einen ganzen
> Datensatz aufs Mal kopieren kann.
Ich kenne nicht Deine exakten DB-Strukturen, bin aber ziemlich sicher, dass
man das Problem sehr wohl mit entsprechenden SQL-Statements, wie z.B. Insert
Into usw. lösen könnte.
> Die Sache mit der PropertyBag hat mich eher verständnishalber für
> zukünftige Problem interessiert (man gibt sich ja Mühe noch etwas
> dazuzulernen...).
Soweit ich es mitbekommen habe, arbeitest Du nicht mit VB sondern mit VBA
und somit hast Du eben gar kein PropertyBag zur Verfügung.