Benutzer mit den meisten Antworten
ADO Recordset in EXCEL

Frage
-
Hallo zusammen,
ich bin gerade dabei, eine Excel 2010 automatisierung mittels VBA zu realisieren. Nun habe ich leider ein (hoffentlich) kleines Problem.Für den Datenbankzugriff auf einen SQL Server 2008 Standard verwende die Microsoft ActiveX Data Objects 6.1 Library. Ich verwende folgenden Code:
Dim connection As New ADODB.Connection Dim recordset as New ADODB.Recordset connection.Open <connectionstring> recordset.Open <sqlQuery>, connection Worksheets("Tabelle2").Range("A2").CopyFromRecordset recordset
Zunächst möchte ich betonen, dass der SQL Server gefunden, und die Query die abgeschickt wird, im SQL Server Management Studio einwandfrei durchläuft. Der Fehler sollte als nicht am ConnectionString oder am SQL liegen.
Das Programm läuft bis zur letzten Zeile durch. Dort erhalte ich die Fehlermeldung "Der Vorgang für ein geschlossenes Objekt nicht zugelassen" (Laufzeitfehler '3704'). Und tatsächlich, das Recordset ist noch geschlossen...die Frage die mich plagt ist...WARUM?
Vielen Dank für eure Hilfe :)
Viele Grüße Holger M. Rößler
Antworten
-
Hallo Holger,
das ist keine Abfrage/View. Diese kannst Du so nicht sinnvoll aus ADO heraus verwenden bzw. wird das erheblich schwieriger und macht Probleme.
Kapsele das, was Du schreibst, in eine Funktion oder Stored Procedure und ruf die dann aus ADO heraus auf, so dass nur noch:
SELECT ... FROM ... WHERE ...
in deinem Programm vorkommt.
Dass das im SQL Server Management Studio funktioniert, hat nichts zu bedeuten. Dein Recordset ist dafür da, Datenlisten abzubilden und nicht, Code auszuführen, Variablen zu definieren, usw.
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community
- Bearbeitet Stefan FalzModerator Freitag, 8. Januar 2016 09:45
- Als Antwort markiert Holger M. Rößler Freitag, 8. Januar 2016 10:36
Alle Antworten
-
Hallo Holger,
poste doch mal bitte dein SQL Statement.
Dann prüf bitte, ob die Connection geöffnet ist. Setz einfach einen Breakpoint auf die Zeile, gehe dann zeilenweise durch und schau, welchen Status die einzelnen Objekte haben.
Falls eigentlich alles passt, probier mal, bei recordset.Open noch zwei weitere Parameter anzugeben. Bspw. so:
recordset.Open <sqlQuery>, connection, 3, 1
Siehe dazu auch:
https://msdn.microsoft.com/de-de/library/ms675544.aspx
https://msdn.microsoft.com/de-de/library/ms681771.aspx
https://msdn.microsoft.com/de-de/library/ms680855.aspx
Das sollte zwar nichts damit zu tun haben aber falls alles andere nicht hilft, ... :)
Ansonsten fällt mir eigentlich nur ein, dass Du es mal so probieren kannst:
Dim connection As ADODB.Connection Dim recordset As ADODB.Recordset Set connection = New ADODB.Connection() Set recordset = New ADODB.Recordset() connection.Open <connectionstring> recordset.Open <sqlQuery>, connection Worksheets("Tabelle2").Range("A2").CopyFromRecordset recordset recordset.Close Set recordset = Nothing connection.Close Set connection = Nothing
Ob "Set" in VBA notwendig ist, weiß ich aber grade nicht, dafür mache ich damit zu wenig.
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community -
Hallo Stefan,
vielen Danke für deine Antwort.Die Query ist recht komplex. Ich bin mir gar nicht sicher, ob ich diese überhaupt posten kann. Aber wie bereits erwähnt tritt während des Abrufs dieser Daten kein Fehler auf, und im Management Studio kommen die erwarteten Daten auch zurück.
Das habe ich alles schon probiert. Ich habe auch schon das Command Objekt verwendet, die Query über die Connection verschickt etc.
Leider immer gleiches Ergebnis :-/
Viele Grüße Holger M. Rößler
-
Hallo Alexander,
auch an dich erst mal vielen Dank, dass du dich meines Problems annimmst.Nein, es sind alles kurze Zeichenketten, also nichts mir Überlänge o.ä.
Die Query liefert 1500 Datensätze. Es ist keine Prozedur, sondern eine Query.
Set NOCOUNT ist nicht in der Query. Query läuft nicht in einen Timeout.Viele Grüße Holger M. Rößler
-
Hallo Holger,
um den Fehler besser eingrenzen zu können, könntest Du zunächst mal das übertragen in das Worksheet, Zeile für Zeile in Einzelschritten vornehmen, das hat den Vorteil, dass man im Debugger besser sehen kann wo der Fehler auftritt.
Hier mal der VBA-Code dazu:
Dim connection As New ADODB.connection Dim recordset As New ADODB.recordset Dim Feld As ADODB.Field Dim Zeile As Integer Dim Spalte As Integer Dim Zelle As Range Dim ws As Worksheet connection.Open "..." recordset.Open "...", connection Set ws = Worksheets("Tabelle2") recordset.MoveFirst Zeile = 1 Do Spalte = 1 For Each Feld In recordset.Fields Spalte = Spalte + 1 Set Zelle = ws.Cells(Zeile, Spalte) Zelle.Value = Feld.Value Next Feld Zeile = Zeile + 1 recordset.MoveNext Loop Until recordset.EOF
Grüße
Roland
-
Hallo Roland,
vielen Dank für deinen Tipp.Leider bekomme ich den gleichen Fehler bei recordset.MoveFirst. Ich habe nun mal eine einfache Query ablaufen lassen (Einfaches select from <table>). Das funktioniert.
Wahrscheinlich ist die Query einfach zu komplex.
Viele Grüße Holger M. Rößler
-
Hallo Holger,
dann poste doch mal bitte dein SQL Statement so, wie es letztendlich an die Datenbank gesendet werden würde. (Also den vollständigen String, falls Platzhaltern/Parameter vorkommen, diese mit den richtigen Werten ersetzen)
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community -
Hallo Holger,
verwende fürs Recordset als CursorType adOpenStatic (+ adLockReadOnly). Denn serverseitige Cursor können nunmal kein MoveFirst.
Das mag auch dazu führen, dass CopyFromRecordset funktioniert, zumindest sollte man die Daten sehen.
Gruß Elmar
-
Hi Stefan,
SET DATEFORMAT YMD DECLARE @YEAR INT SET @YEAR = CONVERT(INT,YEAR(GETDATE())) print CONVERT(INT,YEAR(GETDATE())) DECLARE @MONTH DATETIME SET @MONTH = MONTH(GETDATE()) DECLARE @FIRSTOFMONTH DATETIME SET @FIRSTOFMONTH = GETDATE() print CONVERT(NVARCHAR(8),@FIRSTOFMONTH,112) DECLARE @LASTOFMONTH DATETIME SET @LASTOFMONTH = DATEADD(DAY, -(DAY(DATEADD(MONTH,1, GETDATE()))), DATEADD(MONTH,1, GETDATE())) print CONVERT(NVARCHAR(8),@LASTOFMONTH,112) DECLARE @FIRSTNEXTMONTH DATETIME SET @FIRSTNEXTMONTH = DATEADD(day,1,@LASTOFMONTH) print CONVERT(NVARCHAR(8),@FIRSTNEXTMONTH,112) DECLARE @LASTNEXTMONTH DATETIME SET @LASTNEXTMONTH = DATEADD(DAY, -(DAY(DATEADD(MONTH,1, @FIRSTNEXTMONTH))), DATEADD(MONTH,1, @FIRSTNEXTMONTH)) print CONVERT(NVARCHAR(8),@LASTNEXTMONTH,112) DECLARE @FIRSTINTWOMONTH DATETIME SET @FIRSTINTWOMONTH = DATEADD(day, 1, @LASTNEXTMONTH) print CONVERT(NVARCHAR(8),@FIRSTINTWOMONTH,112) DECLARE @LASTOFYEAR DATETIME SET @LASTOFYEAR = CONVERT(NVARCHAR(4),YEAR(GETDATE()))+ '1231' print CONVERT(NVARCHAR(8),@LASTOFYEAR,112) declare @tmpUrlaub table (KSTNR int, Nachname nvarchar(50), Vorname nvarchar(50), PNR int, GLZANF decimal(10,2), GLZAKT decimal(10,2), GLZDIFF decimal(10,2),URLANS int, URLVOR int, URLSUM int, URLGEN int, URLPLAN1 int, URLPLAN2 int, URLPLANREST int, URLREST int, URLUNK int, PGNR int) USE tisoware_production insert into @tmpUrlaub SELECT p.KSTNR, p.PSNACHNA AS Nachname, p.PSVORNA AS Vorname, p.PSPERSNR AS PNR, -- SubSELECT GLZ-Stand 01.01.2012 'GLZANF' = ISNULL((SELECT CONVERT(DECIMAL(10,2),(Zeitkont.ZKGLEVVM)/60.0) FROM ZEITKONT WHERE ZEITKONT.JAHR = CONVERT(NVARCHAR(4),YEAR(GETDATE())) AND ZEITKONT.MONAT = 1 AND p.PSNR = ZEITKONT.PSNR),0), --Ausgabe des aktuellen GLZ-Standes CONVERT(DECIMAL(10,2),((Zeitkont.ZKGLEVVM + Zeitkont.ZKGLEUMB + Zeitkont.ZKGLELFM)/60.0)) AS 'GLZAKT', 0.0 as GLZDIFF, URLKON.VLFDTAG AS 'URLANS', URLKON.VVORTAG - URLKON.VEVORTAG AS 'URLVOR', URLKON.VLFDTAG +(URLKON.VVORTAG - URLKON.VEVORTAG) AS 'URLSUM', URLKON.GLFDTAG + URLKON.GVORTAG AS 'URLGEN', -- SubSELECT Urlaubstage im 1. Monat URLPLAN1 = (SELECT COUNT(PSNR)-COUNT(kfeidate) FROM ABWKAL LEFT JOIN KALFEIER ON KFEIDATE = ABWDATE AND KALFEIER.LANR = 'BW' WHERE ABWDATE BETWEEN CONVERT(NVARCHAR(8),@FIRSTOFMONTH,112) AND CONVERT(NVARCHAR(8),@LASTOFMONTH,112) AND LOANR = 'URL' AND abwkal.ABWSOLL >0 AND abwkal.PSNR = p.PSNR), -- SubSELECT Urlaubstage im 2. Monat URLPLAN2 = (SELECT COUNT(PSNR)-COUNT(kfeidate) FROM ABWKAL LEFT JOIN KALFEIER ON KFEIDATE = ABWDATE AND KALFEIER.LANR = 'BW' WHERE ABWDATE BETWEEN CONVERT(NVARCHAR(8),@FIRSTNEXTMONTH,112) AND CONVERT(NVARCHAR(8),@LASTNEXTMONTH,112) AND LOANR = 'URL' AND abwkal.ABWSOLL >0 AND abwkal.PSNR = p.PSNR), -- SubSELECT Urlaubstage des restlichen Jahres URLPLANREST = (SELECT COUNT(PSNR)-COUNT(kfeidate) FROM ABWKAL LEFT JOIN KALFEIER ON KFEIDATE = ABWDATE AND KALFEIER.LANR = 'BW' WHERE ABWDATE BETWEEN CONVERT(NVARCHAR(8),@FIRSTINTWOMONTH,112) AND CONVERT(NVARCHAR(8),@LASTOFYEAR,112) AND LOANR = 'URL' AND abwkal.ABWSOLL >0 AND abwkal.PSNR = p.PSNR), -- Resturlaub Start (URLKON.VLFDTAG +(URLKON.VVORTAG - URLKON.VEVORTAG)) - (URLKON.GLFDTAG + URLKON.GVORTAG) - ((SELECT COUNT(PSNR)-COUNT(kfeidate) FROM ABWKAL LEFT JOIN KALFEIER ON KFEIDATE = ABWDATE AND KALFEIER.LANR = 'BW' WHERE ABWDATE BETWEEN CONVERT(NVARCHAR(8),@FIRSTOFMONTH,112) AND CONVERT(NVARCHAR(8),@LASTOFMONTH,112) AND LOANR = 'URL' AND abwkal.ABWSOLL >0 AND abwkal.PSNR = p.PSNR)) - (SELECT COUNT(PSNR)-COUNT(kfeidate) FROM ABWKAL LEFT JOIN KALFEIER ON KFEIDATE = ABWDATE AND KALFEIER.LANR = 'BW' WHERE ABWDATE BETWEEN CONVERT(NVARCHAR(8),@FIRSTNEXTMONTH,112) AND CONVERT(NVARCHAR(8),@LASTNEXTMONTH,112) AND LOANR = 'URL' AND abwkal.ABWSOLL >0 AND abwkal.PSNR = p.PSNR) AS 'URLREST',0.0 as URLUNK -- Resturlaub Ende , p.PGNR FROM PERSTAMM AS p INNER JOIN URLKON ON p.PSNR = URLKON.PSNR INNER JOIN ZEITKONT ON p.PSNR = Zeitkont.PSNR -- PSAUSDAT muss immer aktuelles Datum haben WHERE URLKON.JAHR = @YEAR AND -- Nur Personengruppe Gewerbliche, Angestellte + OFK & Personal p.PGNR in (1,2,7) AND p.PSAUSDAT > CONVERT(NVARCHAR(8),@FIRSTOFMONTH,112) AND --Ausgrenzung der Vorstände p.PSPERSNR not in(2012,2019,2016) AND ZEITKONT.JAHR = CONVERT(NVARCHAR(4),YEAR(GETDATE())) AND ZEITKONT.MONAT = MONTH(GETDATE()) ORDER BY KSTNR, PSNACHNA
Danke!Viele Grüße Holger M. Rößler
-
Hallo Holger,
das ist keine Abfrage/View. Diese kannst Du so nicht sinnvoll aus ADO heraus verwenden bzw. wird das erheblich schwieriger und macht Probleme.
Kapsele das, was Du schreibst, in eine Funktion oder Stored Procedure und ruf die dann aus ADO heraus auf, so dass nur noch:
SELECT ... FROM ... WHERE ...
in deinem Programm vorkommt.
Dass das im SQL Server Management Studio funktioniert, hat nichts zu bedeuten. Dein Recordset ist dafür da, Datenlisten abzubilden und nicht, Code auszuführen, Variablen zu definieren, usw.
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community
- Bearbeitet Stefan FalzModerator Freitag, 8. Januar 2016 09:45
- Als Antwort markiert Holger M. Rößler Freitag, 8. Januar 2016 10:36
-
Hallo Stefan,
das war es tatsächlich.Ich bin verwirrt. Die Ausführung geschieht doch durch den SQL Server. Ob das Result jetzt durch eine Query, einer View oder einer SP ermittelt wird, sollte doch ADO egal sein, bzw. es muss es doch gar nicht wissen???!!!???
Trotzdem vielen Dank an dich.
Viele Grüße Holger M. Rößler
-
Hallo Holger,
nö, das ist ADO erst einmal nicht egal. Letztendlich werden die Befehle einzeln ausgeführt und das würde wahrscheinlich mehrere Resultsets ergeben. Die kann man zwar mittels MARS verarbeiten, bringt aber oftmals Probleme mit sich. Siehe dazu u.a.:
http://stackoverflow.com/questions/374444/disadvantages-of-mars-multiple-active-result-sets
Wenn Du ein ADO Recordset füllen willst, steht in deinem SQL Statement sinnvollerweise _nur_
SELECT ... FROM ... WHERE ... ...
und nichts anderes. Eine tiefere Erklärung dazu kann ich dir grad nicht liefern, ggfs. solltest Du dazu aber mal das hier lesen:
https://support.microsoft.com/en-us/kb/245179
Gruß, Stefan
Microsoft MVP - Visual Developer ASP/ASP.NET
http://www.asp-solutions.de/ - Consulting, Development
http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community