Es liegt an einer Endlosschleife
24.09.2016 23:46:05
Zwenn
Hallo Frank,
abgesehen davon, dass die Fragen von Werner mehr als berechtigt sind, möchte ich Dich auf folgende Dinge bezüglich Deines Codes hinweisen. Ich mache das etwas ausführlicher, weil du angegeben hast nur über Basiskenntnisse in VBA zu verfügen.
Grundsätzlich funktioniert Dein Code und macht genau das, was er soll. Nachdem Du nun eine Arbeitsmappe mit Beispieldaten hochgeladen hast, habe ich diese als .xlsm Datei abgespeichert und Deinen Code in ein Modul eingefügt. Makros lassen sich nicht in .xlsx Dateien speichern, achte darauf bitte in Zukunft.
Der Type Mismatch Fehler kommt nicht, weil das Makro mit irgend einem Datum nicht umgehen kann, sondern weil Du eine Endlosschleife programmiert hast, die aufgrund dieses Laufzeitfehlers nur nicht unendlich laufen kann ;-)
Du hast Dich für eine Do Until Schleife entschieden, was ich schonmal sehr löblich finde, weil mir die Auswahl zeigt, dass Du Dich mit den möglichen Schleifentypen auseinander gesetzt hast.
Dein Schleifenkopf besteht aus der Zeile
Do Until s = i
Im Schleifenrumpf veränderst Du aber weder den Inhalt der Variablen s noch den der Variablen i. Deshalb wird Deine Abbruchbedingung s = i niemals erreicht, was eigentlich zu einer Endlosschleife führt. Da Du im Schleifenrumpf aber mit dem Datentyp Date arbeitest, kommt es zum Type Mismatch Fehler, sobald das Makro eigentlich durchgelaufen ist.
Schaut man nach einem Lauf deines Codes (und Wegklicken der Fehlermeldung) in die Tabelle 'Test', stehen dort 35 Daten. Genau die Anzahl, die wirklich übertragen werden soll. Das Makro versucht aber noch das nächste Datum auszulesen und stößt in Zeile 47 der Tabelle 'Grunddaten' auf eine leere Zelle in Spalte 5. Da ein leerer Wert nicht dem Datentyp Date entspricht, kommt es an dieser Stelle zu besagter Fehlermeldung.
Nun könnte ich einfach sagen, Du musst dafür sorgen, dass die Abbruchbedingung der Schleife erreicht wird. Aber Dein Code ist *hüstel* einigermaßen gruselig und ich habe ihn ehrlich gesagt auch erst richtig verstanden, als ich ihn ausgeführt habe. Mit "ihn verstanden" meine ich, ich glaube ich habe verstanden, was Du beim Programmieren gedacht hast.
Da Du Anfänger in dieser Materie bist, nehme ich mir die Zeit und pflücke Deinen Code mal auseinander. Das mache ich nicht, weil ich mit dem Oberlehrer Zeigefinger auf Dich deuten will, sondern weil ich glaube, Du willst es wirklich verstehen und deshalb hat es einen Sinn.
Zunächst einmal der Hinweis, dass der Deklarationsteil der Variablen in das Makro gehört und nicht davor. Das hat etwas mit dem Gültigkeitsbereich von Variablen zu tun. So wie Du sie deklariert hast, sind es globale Variablen, die jedes Sub und jede Function nach belieben verwenden könnte. Globale Variablen sind beim Programmieren zu vermeiden, weil sie den Code unübersichtlich machen. In VBA habe ich einzig keine Möglichkeit gefunden, sie bei der Verwendung von User Forms zu vermeiden, was mir selbst nicht so richtig paßt.
Den Variablen selbst hast Du wenig sprechende Namen gegeben. Das macht den Code schwerer lesbar. Nicht nur für Dich (vor allem nach ein paar Wochen oder mehr), sondern auch für uns hier im Forum. Mit einem Z kann ich erstmal wenig anfangen, bis ich aus dem Quellcode den Kontext sehe. mit Zeile wäre mir gleich klar, worum es geht. Gewöhne Dir am besten direkt an, sprechende Namen zu vergeben. Um nicht zu lange Variablen oder Funktionsnamen zu erhalten, sind auch Abkürzungen erlaubt.
Darüber hinaus ist es ratsam, den Datentyp als Abkürzung vor den eigentlichen Variablennamen zu setzen. Also z.B. nicht Zeile sondern lZeile, wenn der Datentyp long verwendet wird oder iSpalte, wenn der Datentyp Integer verwendet wird.
Du erleichterst Dir das Programmierer-Leben selbst enorm damit.
Die ganzen Select-Anweisungen sind in den allermeisten Fällen überflüssig. Sie brauchen vor allem eine Menge Zeit beim Programmlauf und Du kannst das gleiche Ergebnis erzielen, wenn Du die entsprechenden Objekte wie Tabellen, Zellen, usw. direkt ansprichst.
So hast Du z.B. geschrieben:
Sheets("Grunddaten").Select
i = Cells(Rows.Count, 5).End(xlUp).Row
i = i - 2
Genau das Gleiche erreichst Du mit der Zeile:
i = Sheets("Grunddaten").Cells(Rows.Count, 5).End(xlUp).Row - 2
Darüber hinaus hast Du Konstanten für die Balttnamen eingeführt. Auch das finde ich echt gut. Du machst nur nicht konsequent Gebrauch davon. Denn eigentlich muss die von mir zusammengefasste Version deiner 3 Zeilen lauten:
i = Sheets(Blatt1).Cells(Rows.Count, 5).End(xlUp).Row - 2
Nun zum Kern Deiner Annahme, wie ich glaube. Du hast folgende zwei Zeilen programmiert und bist dabei Vermutlich davon ausgegangen, die Zeilen-Grenzen festzulegen, in denen Dein Makro arbeiten soll. (Das ist der Teil, den ich glaube in Deiner Denkweise verstanden zu haben.)
Set LaufzBeginn = Range(Cells(2, 5), Cells(i, 5))
Set LaufzEnde = Range(Cells(2, 6), Cells(i, 6))
Du kannst dieses Konstrukt zwar grundsätzlich so verwenden, aber nur die beiden Zeilen reichen nicht aus. Du definierst die Bereiche damit nur, wendest sie in der Folge aber gar nicht an. Sowohl die Variable LaufzBeginn, wie auch LaufzEnde, werden nicht mehr verwendet. Dabei enthält vor allem LaufzEnde den Wert, den Du in Deiner Schleife als Abbruchbedingung verwenden müsstest. Naja, fast ;-) Cells(i, 6) ist der Teil, den Du abfragen musst, woraus sich ergibt, dass Du die beiden Variablen LaufzBeginn und LaufzEnde eigentlich gar nicht brauchst.
Ebenso wenig brauchst Du die beiden folgenden If-Abfragen:
If D Jahr Then
End If
Diese beiden Konstrukte haben gar keinen Rumpf mit Anweisungen, die beim Zutreffen der Bedingung ausgeführt werden sollen. Deshalb sind sie komplett überflüssig und Du kannst sie ersatzlos löschen. Du musst nicht alle Möglichkeiten prüfen, sondern nur die, auf die es ankommt. Das machst Du mit:
If D = Jahr = True Then
Das funktioniert zwar (was ich schon fast lustig finde), ist aber des Guten ein Gleichheitszeichen zuviel ;-)
If D = Jahr Then
reicht völlig aus, denn der Vergeleich ergibt entweder true, wenn beide Werte gleich sind oder false, wenn nicht. Die explizite Abfrage auf einen der beiden Wahrheitswerte ist an dieser Stelle überflüssig.
Ich gebe an dieser Stelle keine Lösung an, weil Werner die eigentlich relevanten Fragen gestellt hat, die Du zunächst mal beantworten solltest
Viele Grüße,
Zwenn