{"id":55000530,"date":"2007-12-01T00:00:00","date_gmt":"2021-02-11T21:19:06","guid":{"rendered":"http:\/\/access-im-unternehmen.aix-dev.de\/aiu\/?p=530"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-30T00:00:00","slug":"Datenimport_aus_Excel","status":"publish","type":"post","link":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/","title":{"rendered":"Datenimport aus Excel"},"content":{"rendered":"<p><img loading=\"lazy\" decoding=\"async\" src=\"http:\/\/vg02.met.vgwort.de\/na\/5497823329934ad9afb6a5f5c3e81860\" width=\"1\" height=\"1\" alt=\"\"><\/p>\n<p><b>Es ist eine immer wiederkehrende Aufgabenstellung: Daten werden als Excel-Datei geliefert und sollen in eine Datenbank eingelesen werden. Viele Access-Entwickler verwenden hier gerne die Methode DoCmd.TransferSpreadsheet. Diese st&ouml;&szlig;t allerdings schnell an ihre Grenzen. Dieser Beitrag zeigt, welche M&ouml;glichkeiten es jenseits dieser Grenzen gibt &#8211; insbesondere dann, wenn die Importdaten im weit verbreiteten Kraut-und-R&uuml;ben-Format vorliegen.<\/b><\/p>\n<\/div>\n<div class=\"story\">\n<p>Die <b>TransferSpreadsheet<\/b>-Methode weist im Wesentlichen zwei Einschr&auml;nkungen auf: Sie steht nur innerhalb von Access-Anwendungen zur Verf&uuml;gung &#8211; wenn Sie eine Anwendung als VB-Projekt entwerfen oder dorthin migrieren m&ouml;chten, m&uuml;ssen Sie die Sache selbst in die Hand nehmen &#8211; und es ist begrenzt auf einzulesende Daten, die bereits ein relationales Format aufweisen, die also so vorliegen, wie sie auch in der Datenbank erscheinen sollen. Diese zweite Einschr&auml;nkung betrifft auch andere Methoden, wie zum Beispiel den SQL-Zugriff auf Excel &uuml;ber den ISAM-Treiber oder das direkte Abfragen einer Excel-Datei &uuml;ber ein DAO- oder ADODB-Recordset.<\/p>\n<p>Beim Verarbeiten der Importdaten spielen die Modulo- und Integerdivision eine entscheidende Rolle. Falls Sie mit diesen Operatoren nicht gut vertraut sind, finden Sie im folgenden Teil eine kleine Erl&auml;uterung. Wenn Sie auf diesem Gebiet jedoch firm sind, k&ouml;nnen Sie auch direkt zur ersten Aufgabenstellung springen.<\/p>\n<p class=\"zwischen-berschrift-oberer-spaltenrand\">Kleine Einf&uuml;hrung: Ganzzahldivision<\/p>\n<p>Die Modulo- (Mod) und Integer-Division (\\) sind Ganzzahloperationen, die Folgendes leisten:<\/p>\n<ul>\n<li class=\"aufz-hlung\"><b>a \\ b <\/b>ergibt die gr&ouml;&szlig;te ganze Zahl, die multipliziert mit <b>b <\/b>kleiner oder gleich <b>a <\/b>ist.<\/li>\n<li class=\"aufz-hlung\"><b>a Mod b <\/b>ergibt den Rest dieser Division.<\/li>\n<\/ul>\n<p>Einige Beispiele:<\/p>\n<pre>5 \\ 2 = 2\r\n5 Mod 2 = 1\r\n14 \\ 5 = 2\r\n14 Mod 5 = 4<\/pre>\n<p>Das ist Division der zweiten Grundschulklasse: zwei passt zweimal in f&uuml;nf, Rest eins; f&uuml;nf passt zweimal in 14, Rest vier. Was Sie damit anfangen k&ouml;nnen, zeigt sich aber viel anschaulicher anhand einer kleinen Schleife:<\/p>\n<pre>For i = 0 To 15\r\n Debug.Print i \\ 5; i Mod 5\r\nNext i<\/pre>\n<p>F&uuml;r <b>\\ <\/b>ergibt sich die Zahlenreihe 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, f&uuml;r <b>Mod <\/b>0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0. Mit der Basis 3 statt 5 erg&auml;be sich f&uuml;r <b>\\ <\/b>die Reihe 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3 &#8230; und f&uuml;r <b>Mod <\/b>0, 1, 2, 0, 1, 2, 0, 1, 2 &#8230;<\/p>\n<p>Schauen Sie sich nun die Indexpaare eines Arrays aus Tabelle 1 an. Wenn Sie die Zellen zeilenweise durchnumerieren, lauten die Zeilenindizes (1, 1, 1, 2, 2, 2, 3, 3, 3) und die Spaltenindizes (1, 2, 3, 1, 2, 3, 1, 2, 3). Das sind genau die um eins erh&ouml;hten Ergebnisse von <b>Zellennummer \\ 3 <\/b>und <b>Zellennummer Mod 3<\/b>.<\/p>\n<div class=\"story\">\n<table class=\"table-style-3\">\n<tbody>\n<tr>\n<td class=\"aiu-zeile1\">\n<\/td>\n<td class=\"aiu-zeile1\">\n<\/td>\n<td class=\"aiu-zeile1\">\n<\/td>\n<\/tr>\n<tr>\n<td class=\"aiu-zeile1\">\n<\/td>\n<td class=\"aiu-zeile1\">\n<\/td>\n<td class=\"aiu-zeile1\">\n<\/td>\n<\/tr>\n<tr>\n<td class=\"aiu-zeile1\">\n<\/td>\n<td class=\"aiu-zeile1\">\n<\/td>\n<td class=\"aiu-zeile1\">\n<\/td>\n<\/tr>\n<tr>\n<td  colspan=\"2\">\n<p><b><span style=\"color:darkgrey\">Tab. 1: Feldindizes einer Tabelle<\/span><\/b><\/p>\n<\/td>\n<td>\n                <\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>Durch Verschiebung und &uuml;berlagerung solcher Indexfolgen durch Multiplikation, Addition und mehrfacher Integerdivision mit verschiedenen Basen k&ouml;nnen Sie aus einem laufenden Z&auml;hler beliebige Indexpaare erzeugen, mit denen Sie die Reihenfolge zum Schreiben der Daten aus einem Array in Felder und Datens&auml;tze festlegen &#8211; Beispiele dazu folgen.<\/p>\n<p><b>Beispiel 1: Daten aus Kreuztabellen einlesen<\/b><\/p>\n<p>Viel &ouml;fter als ordentlich angeordnete Daten mit Spalten f&uuml;r Felder und Zeilen f&uuml;r Datens&auml;tze liefern Kunden ein datentechnisches Tohuwabohu, das Sie nur per Excel-Automation umsetzen k&ouml;nnen (siehe Bild 1).<\/p>\n<div class=\"abbildung\">\n    <img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_06\/Excel-web-images\/Bild363_opt.jpeg\" alt=\"missing image file\" \/>\n<\/div>\n<div class=\"story\">\n<p><b><span style=\"color:darkgrey\">Bild 1: So sehen die zu importierenden Daten aus.<\/span><\/b><\/p>\n<\/div>\n<p>Die Daten sollen die Entwicklung der Quartalsums&auml;tze in verschiedenen Kategorien (Konten, Vertriebsbereiche) von mehreren Filialen darstellen. Die eigentlichen Kategorien (<b>Kategorie##<\/b>) sind noch einmal Oberkategorien (<b>Kategorie#<\/b>) untergeordnet, die jeweils die Summe der untergeordneten Kategorien darstellen.<\/p>\n<p>Sie konnten dem Kunden folgende Zusicherungen abringen: <\/p>\n<ul>\n<li class=\"aufz-hlung\">In Zeile 1 stehen immer die Filialnamen. F&uuml;r jede Filiale werden immer Zahlen f&uuml;r alle vier Quartale geliefert. Zwischen je zwei Filialnamen sind also sicher drei Leerzellen.<\/li>\n<li class=\"aufz-hlung\">In Zeile 2 stehen die Quartalsbezeichnungen. Diese sind nicht fix, sondern k&ouml;nnen ohne Vorank&uuml;ndigung auch <b>1Q<\/b>, <b>1.Quartal<\/b>, <b>Q1<\/b>, <b>Quartal eins <\/b>et cetera lauten. Die Importroutine soll das klaglos verdauen und in der DB in jedem Fall die Zahlen <b>1<\/b>, <b>2<\/b>, <b>3 <\/b>und <b>4 <\/b>f&uuml;rs Quartal ausgeben.<\/li>\n<\/ul>\n<p>Bekannte Zahlen f&uuml;r abgelaufene Quartale, die je Filiale bereits vollst&auml;ndig verarbeitet wurden, sind durch einen gr&uuml;nen Hintergrund in der Quartalsbezeichnung gekennzeichnet. F&uuml;r in der Zukunft liegende beziehungsweise noch nicht verarbeitete Quartale werden dennoch Zahlen geliefert; es handelt sich um extrapolierte Sch&auml;tzungen der Controlling-Abteilung. Solche Werte sind an einem roten Hintergrund erkennbar. In der Datenbank sollen die Werte durch Flags wie <b>Gemessen <\/b>oder <b>Gesch&auml;tzt<\/b> gekennzeichnet werden.<\/p>\n<p>Spalte A enth&auml;lt die Kategorienamen. Oberkategorien werden als berechnete Werte nicht in die Datenbank &uuml;bernommen. Oberkategorien sind an Fettschrift erkennbar.<\/p>\n<p>Das Zielformat in der Datenbank sieht also aus wie in Bild 2 &#8211; hier dargestellt unter Excel.<\/p>\n<div class=\"abbildung\">\n    <img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_06\/Excel-web-images\/Bild376_opt.jpeg\" alt=\"missing image file\" \/>\n<\/div>\n<div class=\"story\">\n<p><b><span style=\"color:darkgrey\">Bild 2: Zielformat<span class=\"annotation-reference\"> der Tabelle aus Bild 1<\/span><\/span><\/b><\/p>\n<\/div>\n<p>Diese Aufgabe ist in keiner Weise mit <b>TransferSpreadsheet <\/b>oder anderen relationalen Methoden l&ouml;sbar, zumal einige der abzubildenden Informationen in Form von Zellformatierungen geliefert werden.<\/p>\n<p>Um die Tabelle im Beispiel zu verarbeiten, ist zun&auml;chst ein wenig Kenntnis der Objekthierarchie von Excel n&ouml;tig.<\/p>\n<p>Zuoberst steht das <b>Application<\/b>-Objekt, das die Anwendung darstellt. Darunter findet sich ein <b>WorkBook<\/b>-Objekt &#8211; das ist die zu verarbeitende Excel-Mappe. In dieser wiederum m&uuml;ssen Sie das Tabellenblatt, das die Daten enth&auml;lt, identifizieren, was mit einem <b>WorkSheet<\/b>-Objekt geschieht. Hier sind jetzt noch der genaue Zellbereich beziehungsweise die einzelnen Zellen, welche die Daten liefern, zu identifizieren. Letzteres geschieht durch ein <b>Range<\/b>-Objekt. Die Hierarchie sieht also wie folgt aus:<\/p>\n<pre>Application\r\n    Workbooks(\"Mappename\")\r\n        WorkSheets(\"Blattname\")\r\n            Range(&lt;Zellbereich&gt;)<\/pre>\n<p class=\"zwischen-berschriftnachquellcode\">Importieren mit der Cells-Eigenschaft<\/p>\n<p>Nachfolgend sehen Sie zun&auml;chst die ben&ouml;tigten Deklarationen (den kompletten Code finden Sie im Modul <b>ImportPivot <\/b>der Beispieldatenbank, die importierte Tabelle hei&szlig;t <b>Pivot <\/b>und ist in der Excel-Datei <b>Daten.xls <\/b>zu finden). Wenn Sie hier das <b>Application<\/b>-Objekt vermissen: Da beim Import keine visuelle Interaktion des Benutzers mit der einzulesenden Excel-Datei erforderlich ist, wird Ihnen hier ein Verfahren vorgestellt, das mit einem Minimum an Code alle Vorg&auml;nge komplett im Hintergrund ausf&uuml;hrt.<\/p>\n<pre>Dim xlWbk As Excel.Workbook\r\nDim xlSht As Excel.Worksheet\r\nDim xlRng As Excel.Range\r\nDim Path As String\r\nDim LastCol As Long\r\nDim LastRow As Long\r\nDim c As Long, r As Long\r\nDim rcs As DAO.Recordset<\/pre>\n<p>Zun&auml;chst m&uuml;ssen Sie nat&uuml;rlich den Pfad der einzulesenden Datei bekannt geben, was je nach Anforderung relativ zur Programmdatei mit einem festen Pfad oder einem <b>Datei-&ouml;ffnen<\/b>-Dialog geschehen kann. Damit k&ouml;nnen Sie direkt und ohne Umst&auml;nde ein <b>Workbook<\/b>-Objekt, das die Mappe darstellt, erzeugen. Davon leiten Sie ein <b>Worksheet<\/b>-Objekt zur Darstellung des Tabellenblatts mit den Daten ab:<\/p>\n<pre>Path = \"Pfad\\Daten.xls\"\r\nSet xlWbk = CreateObject(Path)\r\nSet xlSht = _\r\n     xlWbk.Worksheets(\"Pivot\")<\/pre>\n<p>Die folgenden Anweisungen ermitteln die letzte beschriebene Spalte und Zeile des Datenbereichs, damit Sie bez&uuml;glich der Datenmenge v&ouml;llig flexibel sind. Das dort aufgebaute Recordset identifiziert die Zieltabelle und dient dazu, die Daten dort einzutragen:<\/p>\n<pre>LastRow = xlSht.Cells(&amp;H10000, 1).End(xlUp).Row\r\nLastCol = xlSht.Cells(2, &amp;H100&amp;).End(xlToLeft).Column<\/pre>\n<p>Die eigentliche Arbeit leistet die Schleife aus Listing 1. Die Variable <b>c <\/b>durchl&auml;uft die Spaltenindizes, <b>r <\/b>die Zeilenindizes. Mit der <b>Cells<\/b>-Methode des <b>Worksheet<\/b>-Objekts wird dynamisch jeweils ein <b>Range<\/b>, der nur eine Zelle enth&auml;lt, identifiziert. Die Schleife beginnt mit <b>c = 2 <\/b>und <b>r = 3<\/b>, also der Zelle <b>B3<\/b>, weil dort die Nutzdaten anfangen (siehe Bild 2).<\/p>\n<div class=\"abbildungrahmen\">\n    <img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_06\/Excel-web-images\/Bild382_opt.jpeg\" alt=\"missing image file\" \/>\n<\/div>\n<div class=\"story\">\n<p><b><span style=\"color:darkgrey\">Bild 3: Weiteres Beispiel eines Importformats &#8211; diesmal ohne durch Formatierungen definierte Informationen<\/span><\/b><\/p>\n<\/div>\n<div class=\"abbildung\">\n<p class=\"tabellenkopf\">Listing 1: Auslesen von Daten aus einer pivot-artigen Tabelle<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">Set rcs = CurrentDb.OpenRecordset _<br \/>    (&#8222;SELECT * FROM tblImport WHERE ID Is Null&#8220;)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">With xlSht<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    For c = 2 To LastCol<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        For r = 3 To LastRow<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">            If .Cells(r, 1).Font.Bold = False Then<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">                rcs.AddNew<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">                rcs.Fields(&#8222;Kategorie&#8220;).Value = .Cells(r, 1).Value<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">                rcs.Fields(&#8222;Quartal&#8220;).Value = ((c &#8211; 2) Mod 4) + 1<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">                rcs.Fields(&#8222;Filiale&#8220;).Value = _<br \/>                    .Cells(1, 4 * ((c &#8211; 2) \\ 4) + 2).Value<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">                rcs.Fields(&#8222;Wert&#8220;).Value = .Cells(r, c).Value<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">                If .Cells(2, c).Interior.Color = 65280 _<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">                    Then rcs.Fields(&#8222;Erhebungsart&#8220;).Value = &#8222;Erhoben&#8220;<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">                If .Cells(2, c).Interior.Color = 255 _<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">                    Then rcs.Fields(&#8222;Erhebungsart&#8220;).Value = &#8222;Gesch&auml;tzt&#8220;<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">                rcs.Update<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">            End If<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        Next r<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    Next c<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">End With<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">rcs.Close<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">Set rcs = Nothing<\/p>\n<\/div>\n<p><!--30percent--><\/p>\n<p><b>.Cells(r, 1).Font.Bold = False<\/b> pr&uuml;ft, ob ein echter Wert oder eine Summe vorliegt &#8211; Letztere wird Ihnen ja als Fett gekennzeichnet und nicht importiert -, die Kategoriebezeichnung steht als Zeilenkopf immer in Spalte 1, daher <b>.Cells(r, 1).Value<\/b>.<\/p>\n<p>Der Quartalswert <b>1 <\/b>bis <b>4 <\/b>wird nicht ausgelesen &#8211; Sie wollen von der verwendeten Bezeichnung unabh&auml;ngig sein -, sondern aus der Spaltennummer berechnet, ausgehend von der ersten Datenspalte und der Regel, dass sich der Wert alle vier Spalten wiederholt: <b>((c &#8211; 2) Mod 4) + 1<\/b><\/p>\n<p>Die Filialbezeichnungen stammen aus der ersten Zeile, stehen aber immer nur &uuml;ber dem ersten Quartal. Dem tr&auml;gt die Formel <b>.Cells(1, 4 x ((c &#8211; 2) \\ 4) + 2).Value <\/b>Rechnung.<\/p>\n<p>Richtig einfach haben Sie es mit den Nutzdaten &#8211; die Formel lautet: <b>.Cells(r, c).Value<\/b><\/p>\n<p>Das Feld <b>Erhebungsart <\/b>ermitteln Sie aus den Hintergrundfarben, also <b>.Cells(2, c).Interior.Color = 65280<\/b> beziehungsweise <b>.Cells(2, c).Interior.Color = 255<\/b>. Der restliche Code betrifft das Recordset und d&uuml;rfte selbsterkl&auml;rend sein.<\/p>\n<p>Diese Methode, bei der mittels <b>Cells(r, c) <\/b>der Reihe nach die relevanten Zellen identifiziert und dar&uuml;ber sowohl deren Werte als auch Formatierungen gelesen werden k&ouml;nnen, ist sehr anschaulich und f&uuml;r kleinere Datenmengen v&ouml;llig ausreichend. Es geht aber auch noch erheblich schneller, wie die folgende fiktive Anforderung zeigt.<\/p>\n<p><b>Beispiel 2: Daten im Kreuzschema einlesen ohne Formatierungen<\/b><\/p>\n<p>Im zweiten Beispiel liegen die Daten zwar immer noch in einem Kreuzschema, aber wenigstens in Textform vor. Dadurch brauchen Sie keine Zellformatierungen mehr auszulesen, sondern m&uuml;ssen nur die Zellinhalte bestimmen. Dadurch k&ouml;nnen Sie eine Methode einsetzen, die die ben&ouml;tigte Laufzeit um einen Faktor 15 bis 25 (bei vielen Daten noch erheblich mehr) reduziert. <\/p>\n<p>Excel bietet die M&ouml;glichkeit, alle Werte eines beliebig gro&szlig;en Zellbereichs einer Variant-Variablen zuzuweisen. Dabei wird automatisch ein zweidimensionales Array erzeugt, das man dann &auml;hnlich wie eben die Tabelle selbst mit einer Schleife durchlaufen kann. Da Arrays auf Grund ihres Speicherdesigns schnelle Strukturen sind, ist ein solcher Durchlauf erheblich z&uuml;giger als einer durch die mit der <b>Cells<\/b>-Eigenschaft immer wieder aufgebaute Objekt-Struktur.<\/p>\n<p>Die Deklarationen dieses Beispiels stimmen im wesentlichen mit denen aus dem ersten Beispiel &uuml;berein, wir beschreiben daher an dieser Stelle nur die wichtigsten Elemente der Routine &#8211; das komplette Listing finden Sie im Modul <b>ImportBlock<\/b>.<\/p>\n<p>Zun&auml;chst belegen Sie eine <b>Range<\/b>-Variable mit dem einzulesenden Zellbereich. Hier ist <b>Cells(1, 1)<\/b>, also <b>A1<\/b>, die Startzelle. Statt eine Endzelle anzugeben, vergr&ouml;&szlig;ern Sie den Bereich ganz bequem mit der Methode <b>Resize(Zeilen, Spalten) <\/b>des <b>Range<\/b>-Objekts auf die erforderliche Gr&ouml;&szlig;e, was sich ausgehend von <b>(1, 1) <\/b>mit <b>LastRow <\/b>und <b>LastCol <\/b>deckt. Um diesen gesamten Zellbereich auszulesen, gen&uuml;gt der Einzeiler <b>v = xlRng.Value<\/b>.<\/p>\n<p>Die Excel-Mappe wird nun nicht mehr ben&ouml;tigt und daher mit <b>Close <\/b>geschlossen. Da zum (unsichtbaren) &ouml;ffnen der Mappe ein Start von Excel unumg&auml;nglich ist, wird es hierbei ganz automatisch wieder geschlossen &#8211; ein Segen der impliziten Anwendungsinstanzierung. Durch diese Ma&szlig;nahme geben Sie je nach Version 20 bis 30 Megabyte Arbeitsspeicher f&uuml;r die laufende Excel-Anwendung frei; im Speicher verbleibt nur ein kompaktes Array, das alle Daten enth&auml;lt.<\/p>\n<pre>Set xlRng = _<br \/>    xlSht.Cells(1, 1).Resize(LastRow, LastCol)\r\nv = xlRng.Value\r\nxlWbk.Close xlDoNotSaveChanges<\/pre>\n<p>Nach dem &uuml;blichen Erstellen eines Recordsets mit der Zieltabelle arbeitet Access die nachfolgend vorgestellte Schleife ab, die nun &uuml;ber das Array l&auml;uft. Dieses automatisch generierte Array, das in der Variant-Variablen <b>v <\/b>enthalten ist, wird von Excel immer einsbasiert und zweidimensional erzeugt, verh&auml;lt sich also so, als sei es wie folgt deklariert worden:<\/p>\n<pre>Dim v(1 To Zeilen, 1 To Spalten)<\/pre>\n<p>Daran k&ouml;nnen Sie nichts &auml;ndern und Sie m&uuml;ssen sich im Code darauf einstellen.<\/p>\n<pre>For i = 1 To LastRow * LastCol\r\n    c = (i - 1) Mod 13 + 1\r\n    r = (i - 1) \\ 13 + 1\r\n    If (c - 1) * (r - 1) &gt; 0 Then\r\n        .AddNew\r\n        .Fields(\"BG\").Value = v(r, 1)\r\n        .Fields(\"Monat\").Value = v(1, c)\r\n        .Fields(\"dtWert\").Value = v(r, c)\r\n        .Update\r\n    End If\r\nNext i<\/pre>\n<p>Diese alternative Methode vermeidet eine Doppelschleife. Zun&auml;chst durchl&auml;uft die Routine die Zellen von <b>(1, 1) <\/b>bis <b>(LastRow, LastCol)<\/b>, was dem Produkt aus <b>LastRow <\/b>x <b>LastCol <\/b>Zellen entspricht. Die ben&ouml;tigten Zeilen- und Spaltenindizes ergeben sich aus der Spaltenzahl &#8211; hier sind es insgesamt 13 f&uuml;r zw&ouml;lf Monate und die Kopfspalte, was aber auch dynamisch aus dem Abstand der ersten Datenspalte und <b>LastCol <\/b>berechnet werden kann. Wenn sich die Spalten alle 13 Zellen wiederholen und sich entsprechend die Zeilennummer alle 13 Zellen um eins erh&ouml;ht, bekommen Sie die entsprechenden Indizes mit den folgenden Formeln der Modulo- und Integerdivision:<\/p>\n<pre> c = (i - 1) Mod 13 + 1\r\n r = (i - 1) \\ 13 + 1<\/pre>\n<p>Da die Nutzdaten in der zweiten Zeile und zweiten Spalte beginnen, brauchen Sie eine zus&auml;tzliche Pr&uuml;fung:<\/p>\n<pre>If c &gt; 1 And r &gt; 1<\/pre>\n<p>Sch&ouml;ner, da nur mit Mathematik auskommend, ist die folgende Variante:<\/p>\n<pre>If (c - 1) * (r - 1) &gt; 0 <\/pre>\n<p>Noch einen obendrauf setzt diese Zeile:<\/p>\n<pre>If r * c &gt; r + c - 1<\/pre>\n<p>Die Zuweisungen an die Felder des Recordsets sehen so aus:<\/p>\n<ul>\n<li class=\"aufz-hlung\">Die Buchungsgruppe <b>BG <\/b>steht immer in der aktuellen Zeile und ersten Spalte, also <b>v(r, 1)<\/b>.<\/li>\n<li class=\"aufz-hlung\">Der Monat, der nat&uuml;rlich auch eine beliebige andere Kategorie sein k&ouml;nnte, steht immer in der aktuellen Spalte und ersten Zeile, also <b>v(1, c)<\/b>.<\/li>\n<li class=\"aufz-hlung\">Der jeweilige Nutzwert steht immer in <b>v(r, c)<\/b>.<\/li>\n<\/ul>\n<p>Der Zeitgewinn der <b>Array<\/b>-Methode gegen&uuml;ber der <b>Cells<\/b>-Methode ist erheblich. Wollten Sie auch das erste Beispiel darauf umstellen, gingen Sie wegen der teilweise als Formatierungen vorliegenden Daten folgenderma&szlig;en vor: Bedeutungstragende Formatierungen kommen in der Kopfspalte und zwei Kopfzeilen vor.<\/p>\n<p>Durchlaufen Sie diese Bereiche mit <b>Cells <\/b>und legen Sie die relevanten Informationen in Hilfs-Arrays ab. Lesen Sie den eigentlichen Datenbereich mit <b>Value <\/b>in ein Variant-Array ein. Bei gro&szlig;en Datenmengen ist das trotz des h&ouml;heren Code-Aufwands immer noch schneller, als alles mit <b>Cells <\/b>durchzugehen.<\/p>\n<p>Angenommen, Sie lesen 50 Spalten &aacute; 2.000 Zeilen ein. Mit dem Vorgehen aus dem ersten Beispiel wenden Sie 100.000 Mal <b>Cells <\/b>an. Mit der hier vorgeschlagenen Kombination wenden Sie 2.050 Mal <b>Cells <\/b>an und einmal <b>Value<\/b>.<\/p>\n<p>Noch besser ist es nat&uuml;rlich, Sie reden dem Kunden aus, Formate als Informationstr&auml;ger zu benutzen, dann wenden Sie in jedem Fall nur einmal <b>Value <\/b>an.<\/p>\n<p>Immer sollten Sie aber ber&uuml;cksichtigen, wie oft der Import tats&auml;chlich stattfindet &#8211; geschieht dies nur einmal, k&ouml;nnen Sie die Performance nat&uuml;rlich eher vernachl&auml;ssigen, als wenn regelm&auml;&szlig;ig Daten importiert werden.<\/p>\n<p class=\"zwischen-berschrift-oberer-spaltenrand\">Import aus Tabellen mit redundanten <br \/>Informationen<\/p>\n<p>Der Import soll jetzt noch etwas schwieriger werden. Ausgehend vom vorherigen Beispiel mit seinen zw&ouml;lf Monatswerten sieht die Quelltabelle nun wie in Bild 4 aus und enth&auml;lt nach jeweils drei Monatsspalten noch eine zus&auml;tzliche Spalte zur Anzeige der Quartalssumme. Diese soll nat&uuml;rlich nicht importiert werden, da ihre Daten von den jeweils links daneben befindlichen Spalten abh&auml;ngig sind und somit in der Datenbank redundant w&auml;ren.<\/p>\n<div class=\"abbildungrahmen\">\n    <img decoding=\"async\" src=\"..\/fileadmin\/_temp_\/2007_06\/Excel-web-images\/Bild388_opt.jpeg\" alt=\"missing image file\" \/>\n<\/div>\n<div class=\"story\">\n<p><b><span style=\"color:darkgrey\">Bild 4: Tabellenblatt f&uuml;r den Import, das zu &uuml;berspringende Spalten enth&auml;lt<\/span><\/b><\/p>\n<\/div>\n<p>Statt umst&auml;ndlich auf Beschriftungen wie <b>Q1 <\/b>in der Kopfzeile zu pr&uuml;fen, die sich unter Umst&auml;nden auch &auml;ndern, m&uuml;ssen Sie f&uuml;r diesen Fall nur den Ausdruck anpassen, der die Spaltenindizes berechnet:<\/p>\n<pre>c = (i - 1) Mod (17 - 4) + 1\r\nr = (i - 1) \\ (17 - 4) + 1<\/pre>\n<p>Beachten Sie den Ausdruck <b>(17 &#8211; 4)<\/b>, der etwa den Ausdruck <b>13 <\/b>ersetzt: Eine Kopfspalte plus zw&ouml;lf Monate plus vier Quartalssummen (der Ausdruck <b>17 &#8211; 4 <\/b>steht hier nat&uuml;rlich nur zur Verdeutlichung: 17 Spalten, von denen vier wegfallen). Das funktioniert nat&uuml;rlich noch nicht, da so nach wie vor die Zahlenreihe von 1 bis 13, also 1, 2, 3 &#8230; 13, erzeugt wird. <\/p>\n<p>Damit alle 17 Spalten unter Auslassung der Summenspalten ber&uuml;cksichtigt werden, erg&auml;nzen Sie den Ausdruck wie folgt:<\/p>\n<pre>For i = 1 To LastRow * (LastCol - 4)\r\n    r = (i - 1) \\ (17 - 4) + 1\r\n    c = (i - 1) Mod (17 - 4) + 1\r\n    c = c + (c - 2) \\ 3<\/pre>\n<p>Der so ge&auml;nderte Code ergibt f&uuml;r <b>c <\/b>die sich wiederholende Zahlenreihe (1, 2, 3, 4, 6, 7, 8, 10, 11, 12, 14, 15, 16). Ein Blick in Bild 4 zeigt: Es fehlen genau die Spalten, die Sie nicht importieren wollen, n&auml;mlich 5 (E, Q1), 9 (I, Q2), 13 (M, Q3) und 17 (Q, Q4). Davon abgesehen verwenden Sie die gleiche Routine wie im vorherigen Beispiel.<\/p>\n<p>Im Detail passiert hier Folgendes: Der Bereich erstreckt sich nun &uuml;ber 17 statt wie vorher 13 Spalten. Aber Sie wollen nach wie vor nur 13 von diesen 17 Spalten importieren, vier werden &uuml;bergangen. Daher sind immer noch <b>lastRow x 13 <\/b>Schritte n&ouml;tig. Das gilt sowohl f&uuml;r den Schleifenkopf, <b>lastCol &#8211; 4 <\/b>ist ja <b>17 &#8211; 4<\/b>, als auch f&uuml;r die Ausdr&uuml;cke f&uuml;r <b>r <\/b>und <b>c<\/b>.<\/p>\n<p>Der <b>c<\/b>-Ausdruck erzeugt nat&uuml;rlich dieselbe Ausgabe, wie er das auf Basis 13 auch schon tat, n&auml;mlich (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13).<\/p>\n<p>Sie brauchen aber stattdessen die Reihe (1, 2, 3, 4, 6, 7, 8, 10, 11, 12, 14, 15, 16). Das erreichen Sie durch Addition von (0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3). Diese Zahlenreihe erzeugt der Ausdruck <b>(c &#8211; 2) \\ 3<\/b>, der deshalb addiert wird.<\/p>\n<p><b>Einlesen von Daten in Blockdarstellung<\/b><\/p>\n<p>Nun setzt Ihnen Ihr Kunde noch einen drauf. Die Daten sollen aus einer Excel-Tabelle in Blockdarstellung importiert werden (siehe Bild 5).<\/p>\n<div class=\"abbildungrahmen\">\n    <img decoding=\"async\" src=\"Excel-web-images\/Bild394_opt.jpeg\" alt=\"missing image file\" \/>\n<\/div>\n<div class=\"story\">\n<p><b><span style=\"color:darkgrey\">Bild 5: Excel-Tabelle im Blockformat<\/span><\/b><\/p>\n<\/div>\n<p>Ein Datensatz kommt hier als Block der Tiefe <b>3 <\/b>und der Breite <b>2 <\/b>daher. Die Bedeutung ist durch die Anordnung festgelegt:<\/p>\n<ul>\n<li class=\"aufz-hlung\">Anrede, Geburtsdatum<\/li>\n<li class=\"aufz-hlung\">Vorname, Gewicht<\/li>\n<li class=\"aufz-hlung\">Nachname, Gr&ouml;&szlig;e<\/li>\n<\/ul>\n<p>Auch diese Aufgabenstellung l&ouml;sen Sie mit einer kleinen Modifikation der vorherigen Beispiele.<\/p>\n<p>Da ein Datensatz drei Zeilen und zwei Spalten belegt, errechnet sich die Anzahl der Schleifendurchl&auml;ufe wie folgt:<\/p>\n<pre>(Bereichstiefe * Bereichsbreite) \/ (Blocktiefe * Blockbreite)<\/pre>\n<p>Die 4 x 12 = 48 Zellen im Beispiel entsprechen 48 \/ 6 = 8 Datens&auml;tzen. Sie m&uuml;ssen also in einer Schleife von 1 bis 8 aus der Z&auml;hlvariablen je 6 Indexpaare erzeugen. Der relevante Code dazu sieht wie in Listing 2 aus.<\/p>\n<div class=\"abbildung\">\n<p class=\"tabellenkopf\">Listing 2: Auslesen von Daten en bloc<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">Set xlWbk = CreateObject(Path)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">Set xlSht = xlWbk.Worksheets(&#8222;Bl&ouml;cke&#8220;)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">LastRow = xlSht.Cells(&amp;H10000, 2).End(xlUp).Row<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">LastCol = xlSht.Cells(2, &amp;H100&amp;).End(xlToLeft).Column<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">Set xlRng = xlSht.Cells(2, 2).Resize(LastRow, LastCol)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">v = xlRng.Value<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">xlWbk.Close xlDoNotSaveChanges<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">Set rcs = CurrentDb.OpenRecordset _<br \/>    (&#8222;SELECT * FROM tblPerson WHERE ID Is Null&#8220;)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">With rcs<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    For i = 1 To (LastRow &#8211; 1) * (LastCol &#8211; 1) \/ 6<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        r = (i &#8211; 1) \\ 2 + 1<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        c = (i &#8211; 1) Mod 2 + 1<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        .AddNew<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        .Fields(&#8222;Anrede&#8220;).Value = v(3 * r &#8211; 2, 2 * c &#8211; 1)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        .Fields(&#8222;Vorname&#8220;).Value = v(3 * r &#8211; 1, 2 * c &#8211; 1)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        .Fields(&#8222;Nachname&#8220;).Value = v(3 * r, 2 * c &#8211; 1)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        .Fields(&#8222;GebDat&#8220;).Value = v(3 * r &#8211; 2, 2 * c)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        .Fields(&#8222;Gewicht&#8220;).Value = v(3 * r &#8211; 1, 2 * c)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        .Fields(&#8222;Groesse&#8220;).Value = v(3 * r, 2 * c)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        .Update<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    Next i<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">End With<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">rcs.Close<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">Set rcs = Nothing<\/p>\n<\/div>\n<p>Da im zweiten Beispiel die Datens&auml;tze nebeneinander stehen, wird analog zu den bisherigen Beispielen der Zeilen- und Spaltenindex mit Modulo- beziehungsweise Integerdivision &uuml;ber der Basis 2 erzeugt. Durch die Blockbildung h&auml;lt hier aber ein <b>r-c<\/b>-P&auml;rchen f&uuml;r sechs Indexwerte her.<\/p>\n<p>Aus dem Zeilenindex 1 wird durch Multiplikation 3, aus dem Zeilenindex 2 wird 6 und so weiter. Es ergeben sich also Dreierschritte. Durch die Subtraktion von 2, 1 und 0 wird so aus 1, 2, &#8230; (1, 2, 3), (4, 5, 6) &#8230; <\/p>\n<p>F&uuml;r die Spalten gilt das Gleiche &#8211; nur mit zwei statt drei Werten -, womit sich durch Kombination alle sechs ben&ouml;tigten Indexpaare ergeben.<\/p>\n<p><b>Zwischenfazit<\/b><\/p>\n<p>Das Prinzip, das hinter dem Linearisieren der Variant-Arrays steht, ist immer dasselbe. Die logische Struktur, die in der Anordnung der Daten steckt, zeigt sich in regelm&auml;&szlig;igen Wiederholungen und Iterationen von Zeilen- und Spaltennummern, die Sie mittels Modulo- und Integerdivision sowie einigen einfachen Grundrechenoperationen nachbilden. Mit etwas &uuml;bung k&ouml;nnen Sie so beliebig komplexe Regeln der Datengeometrie abbilden.<\/p>\n<p><b>Alternative ADODB<\/b><\/p>\n<p>An Stelle des gezeigten Codes mit DAO wollen oder m&uuml;ssen Sie vielleicht mit ADO arbeiten. Hier sind passende Angaben zu <b>LockType<\/b>, <b>CursorLocation <\/b>und <b>CursorType <\/b>eines Recordsets von entscheidender Bedeutung, da Ihnen sonst die Performance ganz erheblich in die Knie gehen kann. Ein Beispiel f&uuml;r sinnvolle Einstellungen zeigt Listing 3.<\/p>\n<div class=\"abbildung\">\n<p class=\"tabellenkopf\">Listing 3: Excel-Daten einlesen per ADO<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">Set rsa = New ADODB.Recordset<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">With rsa<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    Set .ActiveConnection = cnn<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    .LockType = adLockOptimistic<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    .CursorLocation = adUseServer<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    .CursorType = adOpenKeyset<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    .Source = SQL<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    .Open<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    For i = 1 To LastRow * LastCol<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        c = (i &#8211; 1) Mod 13 + 1<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        r = (i &#8211; 1) \\ 13 + 1<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        If (c &#8211; 1) * (r &#8211; 1) &gt; 0 Then<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">            .AddNew<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">            .Fields(&#8222;BG&#8220;).Value = V(r, 1)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">            .Fields(&#8222;Monat&#8220;).Value = V(1, c)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">            .Fields(&#8222;dtWert&#8220;).Value = V(r, c)<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">            .Update<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">        End If<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    Next i<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">    .Close<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">End With<\/p>\n<p class=\"quellcode-funktionssammlung-1-5\">Set rsa = Nothing<\/p>\n<\/div>\n<p>Die schnellste einstellbare ADO-Variante ist beim Jet-Zugriff immerhin nur noch doppelt so langsam wie das DAO-Pendant &#8211; Sie k&ouml;nnen ADO aber mit falschen Einstellungen problemlos bis zur Unertr&auml;glichkeit ausbremsen.<\/p>\n<p>Wenn Sie das Daten-Backend nicht auf der lokalen Maschine, auf der der Import durchgef&uuml;hrt wird, sondern auf einem Netzwerkserver liegen haben, sollten Sie Ihr Recordset nicht auf der <b>CurrentDb <\/b>(DAO) oder <b>CurrentProject.Connection <\/b>(ADO) und somit auf eingelinkten Tabellen aufbauen, sondern eine direkte Verbindung zum Backend herstellen.<\/p>\n<p>Bei DAO l&auml;uft Ihr Code dann etwa drei- bis viermal so schnell, bei ADO ist der Unterschied viel gr&ouml;&szlig;er: Ein mit den Eigenschaften <b>LockType<\/b> und <b>CursorLocation <\/b>optimistisch eingestelltes Recordset auf verkn&uuml;pfte Tabellen braucht bis zu 300 bis 400 Mal l&auml;nger als ein pessimistisch eingestelltes auf dem Server.<\/p>\n<p>Da ADO l&auml;ngst nicht alle Kombinationen von <b>LockType<\/b>, <b>CursorLocation <\/b>und <b>CursorType <\/b>unterst&uuml;tzt &#8211; viele davon w&auml;ren in sich unlogisch -, sollten Sie sich vorsichtshalber mit folgender Zeile davon &uuml;berzeugen, was da jetzt eigentlich herausgekommen ist.<\/p>\n<pre>Debug.Print .LockType; .CursorLocation; .CursorType<\/pre>\n<p>Wenn Sie n&auml;mlich eine unsinnige oder keine Kombination angeben, nimmt ADO selbst eine Einstellung vor, was keineswegs immer optimal ist.<\/p>\n<p><b>Performance<\/b><\/p>\n<p>Zun&auml;chst ein Vergleich der Gesamtlaufzeit des Importvorgangs mit der Methode des zellenweisen Durchlaufens gegen Verwendung eines Arrays. Bei 6.000 einzuf&uuml;genden Datens&auml;tzen ergab sich auf dem Testsystem:<\/p>\n<ul>\n<li class=\"aufz-hlung\">DAO mit Cells: 18,79 s<\/li>\n<li class=\"aufz-hlung\">DAO mit Array: 1,42 s<\/li>\n<\/ul>\n<p>Die <b>Cells<\/b>-Methode ist damit au&szlig;er f&uuml;r geringe Datenmengen klar deklassiert. Die <b>Array<\/b>-Methode gliedert sich in drei entscheidende Schritte &#8211; jeweils mit verbrauchter Zeit:<\/p>\n<ul>\n<li class=\"aufz-hlung\">Start von Excel und &ouml;ffnen der Mappe (800 ms)<\/li>\n<li class=\"aufz-hlung\">F&uuml;llen des Arrays <b>v = xlRng.Value<\/b> (450 ms)<\/li>\n<li class=\"aufz-hlung\">Durchlaufen des Arrays und Schreiben der Daten in das Recordset (170 ms)<\/li>\n<\/ul>\n<p>Die ersten beiden Punkte sind bei ADO nat&uuml;rlich identisch; statt der 170 Millisekunden im dritten Schritt dauert dieser aber immerhin 590 Millisekunden.<\/p>\n<p>Zum Vergleich DAO &#8211; ADO noch einige Zahlen, gemessen mit 600 Datens&auml;tzen &uuml;bers Netz auf ein entferntes Backend. Die angegebenen Zeiten beziehen sich immer nur auf den letzten Schritt, also das Wegschreiben des Arrays mit einem Recordset in einer Schleife.<\/p>\n<p>Die folgenden Werte beziehen sich auf ADO:<\/p>\n<ul>\n<li class=\"aufz-hlung\">Optimistisch gesperrter statischer Client-Cursor auf eingelinkte Tabellen mit <b>CurrentProject.Connection<\/b>: 44.000 ms (44 s!!!)<\/li>\n<li class=\"aufz-hlung\">Optimistisch gesperrter statischer Client-Cursor auf Backend mit direkt auf den Netzwerkpfad erstellter Connection: 850 ms<\/li>\n<li class=\"aufz-hlung\">Batch-optimistisch gesperrter statischer Client-Cursor auf Backend mit direkt auf den Netzwerkpfad erstellter Connection: 840 ms<\/li>\n<li class=\"aufz-hlung\">Pessimistisch gesperrter Keyset-Server-Cursor auf Backend mit direkt auf den Netzwerkpfad erstellter Connection: 122 ms<\/li>\n<\/ul>\n<p>Mit DAO haben wir folgende Werte ermittelt:<\/p>\n<ul>\n<li class=\"aufz-hlung\">Recordset auf eingelinkte Tabellen mit <b>CurrentDb<\/b>: 117 ms<\/li>\n<li class=\"aufz-hlung\">Recordset auf Backend mit direkt auf den Netzwerkpfad erstellter DB-Variable mit <b>OpenDatabase<\/b>: 31 ms<\/li>\n<\/ul>\n<p>Im Modul <b>Performance <\/b>der Beispieldatenbank finden Sie weitere Informationen zu den verwendeten Anweisungen.<\/p>\n<p><b>Zusammenfassung und Ausblick<\/b><\/p>\n<p>Auch Excel-Tabellen, deren Daten sich nicht direkt mit Methoden wie <b>DoCmd.TransferSpreadsheet <\/b>importieren lassen, kann man sich per VBA gef&uuml;gig machen &#8211; die Grundtechniken und einige Beispiele liefert dieser Beitrag.<\/p>\n<p>Aspekte wie Performance und Code-Refactorings sollten Sie zumindest so lange au&szlig;en vor lassen, wie Sie einen Import nur ein einziges Mal durchf&uuml;hren &#8211; etwa, um die Daten bei der Erstellung einer neuen Anwendung erstmalig zu importieren.<\/p>\n<p>Kunden &auml;ndern auch gern mal zwischendurch die Struktur der Excel-Daten &#8211; weisen Sie diese am besten direkt darauf hin, dass dies Extraaufwand f&uuml;r Sie bedeutet. Mit dem Verweis auf dadurch entstehende Mehrkosten bringen Sie den Kunden meist dazu, die aktuelle Struktur beizubehalten.<\/p>\n<h3>Downloads zu diesem Beitrag<\/h3>\n<p>Enthaltene Beispieldateien:<\/p>\n<p>Daten.xls<\/p>\n<p>Import.mdb<\/p>\n<p><a href=\"..\/fileadmin\/beispiele\/DB03CDA7-29CC-4EC7-B8CF-6628799BE098\/aiu_530.zip\">Download<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Es ist eine immer wiederkehrende Aufgabenstellung: Daten werden als Excel-Datei geliefert und sollen in eine Datenbank eingelesen werden. Viele Access-Entwickler verwenden hier gerne die Methode DoCmd.TransferSpreadsheet. Diese st&ouml;&szlig;t allerdings schnell an ihre Grenzen. Dieser Beitrag zeigt, welche M&ouml;glichkeiten es jenseits dieser Grenzen gibt &#8211; insbesondere dann, wenn die Importdaten im weit verbreiteten Kraut-und-R&uuml;ben-Format vorliegen.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[662007,66062007,44000032,44000026,44000025],"tags":[],"class_list":["post-55000530","post","type-post","status-publish","format-standard","hentry","category-662007","category-66062007","category-Excel","category-Interaktiv","category-VBA_und_Programmiertechniken"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v20.9 (Yoast SEO v27.4) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Datenimport aus Excel - Access im Unternehmen<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/\" \/>\n<meta property=\"og:locale\" content=\"de_DE\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Datenimport aus Excel\" \/>\n<meta property=\"og:description\" content=\"Es ist eine immer wiederkehrende Aufgabenstellung: Daten werden als Excel-Datei geliefert und sollen in eine Datenbank eingelesen werden. Viele Access-Entwickler verwenden hier gerne die Methode DoCmd.TransferSpreadsheet. Diese st&ouml;&szlig;t allerdings schnell an ihre Grenzen. Dieser Beitrag zeigt, welche M&ouml;glichkeiten es jenseits dieser Grenzen gibt - insbesondere dann, wenn die Importdaten im weit verbreiteten Kraut-und-R&uuml;ben-Format vorliegen.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/\" \/>\n<meta property=\"og:site_name\" content=\"Access im Unternehmen\" \/>\n<meta property=\"article:published_time\" content=\"2021-02-11T21:19:06+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/vg02.met.vgwort.de\/na\/5497823329934ad9afb6a5f5c3e81860\" \/>\n<meta name=\"author\" content=\"Andr\u00e9 Minhorst\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Verfasst von\" \/>\n\t<meta name=\"twitter:data1\" content=\"Andr\u00e9 Minhorst\" \/>\n\t<meta name=\"twitter:label2\" content=\"Gesch\u00e4tzte Lesezeit\" \/>\n\t<meta name=\"twitter:data2\" content=\"20\u00a0Minuten\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/\"},\"author\":{\"name\":\"Andr\u00e9 Minhorst\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\"},\"headline\":\"Datenimport aus Excel\",\"datePublished\":\"2021-02-11T21:19:06+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/\"},\"wordCount\":3695,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5497823329934ad9afb6a5f5c3e81860\",\"articleSection\":[\"2007\",\"6\\\/2007\",\"Excel\",\"Interaktiv\",\"VBA und Programmiertechniken\"],\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/\",\"name\":\"Datenimport aus Excel - Access im Unternehmen\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/#primaryimage\"},\"thumbnailUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5497823329934ad9afb6a5f5c3e81860\",\"datePublished\":\"2021-02-11T21:19:06+00:00\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/#breadcrumb\"},\"inLanguage\":\"de\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/#primaryimage\",\"url\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5497823329934ad9afb6a5f5c3e81860\",\"contentUrl\":\"http:\\\/\\\/vg02.met.vgwort.de\\\/na\\\/5497823329934ad9afb6a5f5c3e81860\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/Datenimport_aus_Excel\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Datenimport aus Excel\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#website\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"name\":\"Access im Unternehmen\",\"description\":\"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access\",\"publisher\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/access-im-unternehmen.de\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"de\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#organization\",\"name\":\"Andr\u00e9 Minhorst Verlag\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"contentUrl\":\"https:\\\/\\\/access-im-unternehmen.de\\\/wp-content\\\/uploads\\\/2019\\\/09\\\/aiu_wp.png\",\"width\":370,\"height\":111,\"caption\":\"Andr\u00e9 Minhorst Verlag\"},\"image\":{\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/logo\\\/image\\\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/access-im-unternehmen.de\\\/#\\\/schema\\\/person\\\/13395c4bcd7d7963efe33be9c584d93f\",\"name\":\"Andr\u00e9 Minhorst\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"de\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g\",\"caption\":\"Andr\u00e9 Minhorst\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Datenimport aus Excel - Access im Unternehmen","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/","og_locale":"de_DE","og_type":"article","og_title":"Datenimport aus Excel","og_description":"Es ist eine immer wiederkehrende Aufgabenstellung: Daten werden als Excel-Datei geliefert und sollen in eine Datenbank eingelesen werden. Viele Access-Entwickler verwenden hier gerne die Methode DoCmd.TransferSpreadsheet. Diese st&ouml;&szlig;t allerdings schnell an ihre Grenzen. Dieser Beitrag zeigt, welche M&ouml;glichkeiten es jenseits dieser Grenzen gibt - insbesondere dann, wenn die Importdaten im weit verbreiteten Kraut-und-R&uuml;ben-Format vorliegen.","og_url":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/","og_site_name":"Access im Unternehmen","article_published_time":"2021-02-11T21:19:06+00:00","og_image":[{"url":"http:\/\/vg02.met.vgwort.de\/na\/5497823329934ad9afb6a5f5c3e81860","type":"","width":"","height":""}],"author":"Andr\u00e9 Minhorst","twitter_card":"summary_large_image","twitter_misc":{"Verfasst von":"Andr\u00e9 Minhorst","Gesch\u00e4tzte Lesezeit":"20\u00a0Minuten"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/#article","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/"},"author":{"name":"Andr\u00e9 Minhorst","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f"},"headline":"Datenimport aus Excel","datePublished":"2021-02-11T21:19:06+00:00","mainEntityOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/"},"wordCount":3695,"commentCount":0,"publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/#primaryimage"},"thumbnailUrl":"http:\/\/vg02.met.vgwort.de\/na\/5497823329934ad9afb6a5f5c3e81860","articleSection":["2007","6\/2007","Excel","Interaktiv","VBA und Programmiertechniken"],"inLanguage":"de","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/","url":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/","name":"Datenimport aus Excel - Access im Unternehmen","isPartOf":{"@id":"https:\/\/access-im-unternehmen.de\/#website"},"primaryImageOfPage":{"@id":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/#primaryimage"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/#primaryimage"},"thumbnailUrl":"http:\/\/vg02.met.vgwort.de\/na\/5497823329934ad9afb6a5f5c3e81860","datePublished":"2021-02-11T21:19:06+00:00","breadcrumb":{"@id":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/#breadcrumb"},"inLanguage":"de","potentialAction":[{"@type":"ReadAction","target":["https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/"]}]},{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/#primaryimage","url":"http:\/\/vg02.met.vgwort.de\/na\/5497823329934ad9afb6a5f5c3e81860","contentUrl":"http:\/\/vg02.met.vgwort.de\/na\/5497823329934ad9afb6a5f5c3e81860"},{"@type":"BreadcrumbList","@id":"https:\/\/access-im-unternehmen.de\/Datenimport_aus_Excel\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/access-im-unternehmen.de\/"},{"@type":"ListItem","position":2,"name":"Datenimport aus Excel"}]},{"@type":"WebSite","@id":"https:\/\/access-im-unternehmen.de\/#website","url":"https:\/\/access-im-unternehmen.de\/","name":"Access im Unternehmen","description":"Das Magazin f\u00fcr Datenbankentwickler auf Basis von Microsoft Access","publisher":{"@id":"https:\/\/access-im-unternehmen.de\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/access-im-unternehmen.de\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"de"},{"@type":"Organization","@id":"https:\/\/access-im-unternehmen.de\/#organization","name":"Andr\u00e9 Minhorst Verlag","url":"https:\/\/access-im-unternehmen.de\/","logo":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/","url":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","contentUrl":"https:\/\/access-im-unternehmen.de\/wp-content\/uploads\/2019\/09\/aiu_wp.png","width":370,"height":111,"caption":"Andr\u00e9 Minhorst Verlag"},"image":{"@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/access-im-unternehmen.de\/#\/schema\/person\/13395c4bcd7d7963efe33be9c584d93f","name":"Andr\u00e9 Minhorst","image":{"@type":"ImageObject","inLanguage":"de","@id":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/1b9d010cf1716692cb9c34f21554e07d17d461acaea5b61b8cb21cbec678d48a?s=96&d=mm&r=g","caption":"Andr\u00e9 Minhorst"}}]}},"_links":{"self":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55000530","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/comments?post=55000530"}],"version-history":[{"count":0,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/posts\/55000530\/revisions"}],"wp:attachment":[{"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/media?parent=55000530"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/categories?post=55000530"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/access-im-unternehmen.de\/data\/wp\/v2\/tags?post=55000530"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}