Fragensteller
Extrem langsame Excel-Berechnung

Frage
-
Hallo,
unter der Konstellation Microsoft Excel 2013 und Windows 10 tritt bei uns folgendes Problem auf:
Es gibt eine Excel-Datei, welche Daten über ein SQL-Statement aus einer Datenbank abgreift.
Diese Abfragen funktionieren auch problemlos, und solange ich die Berechnung für nur ein Feld ausführe, ist dies innerhalb von Sekundenbruchteilen durchgeführt.
Nun möchte ich allerdings alle Felder des Blattes automatisiert berechnen lassen, hierbei handelt es sich um etwa 100 Felder.
Diese Berechnung dauert nun allerdings bis zu 35 Minuten.Ist hierfür eine Problemlösung bekannt? Eine so lange Berechnungszeit kann ja nicht normal sein!
Grüße,
Alle Antworten
-
Da kann man auch nur spekulieren, was du denn da so berechnest.
Oder holst du innerhalb der Berechnung jeweils wieder neue SQL-Daten?
Wenn eine Formel ggf. iterativ (also recursiv) berechnet wird und dabei jedes mal ein SQL läuft, dann summiert sich das halt.
Aber, wie gesagt, alles nur Spekulation.Bei SQL-Abfragen selber kann es schon mal vorkommen, dass die Abfrage für 1 Satz anders optimiert wird als für viele Sätze. Hier hilft nur eine Prüfung des SQL und ggf. das Anlegen von Indizes auf den Tabellen.
-
Hallo,
leider muss ich hierzu sagen, dass ich den Code nicht selbst geschrieben habe.
Die SQL Abfragen selbst sind folgende:
"select count(*) from abrechnungseinheit ae " _
& "join ausgangsliste al on al.ausgangslistenr = ae.ausgangslistenr " _
& "where cast(al.erstelldatetime as date) = ? and ae.zieladrlkz = ?""select count(*) from ((select lieferungnr from lieferung " & _
"where speicherdatetime between ? and ?) as lf " & _
"join teillieferung tl on lf.lieferungnr = tl.lieferungnr " & _
"join versandeinheit ve on tl.versandeinheitnr = ve.versandeinheitnr " & _
"join abrechnungseinheit ae on ve.abrechnungseinheitnr = ae.abrechnungseinheitnr) " & _
"where zieladrlkz = ? and vestatus is null and labelname <> 'NeutralSpedition'""select count(ve.barcodeinhalt) from ausgangsliste al " & _
"join abrechnungseinheit ae on al.ausgangslistenr = ae.ausgangslistenr " & _
"join versandeinheit ve on ae.abrechnungseinheitnr = ve.abrechnungseinheitnr " & _
"join teillieferung tl on ve.versandeinheitnr = tl.versandeinheitnr " & _
"join lieferung lf on tl.lieferungnr = lf.lieferungnr " & _
"where ae.zieladrlkz = ? and cast(al.erstelldatetime as date) between ? and ? and " & _
"lf.speicherdatetime between ? and ?"Grüße,
-
Auch hier bleibt es bei der Spekulation meinerseits.
Da du 3 Abfragen hast, werden diese auch alle bei deiner Berechnung eines einzelnen Feldes ausgeführt?
Es kann hier durchaus sein, dass die Abfragen mit "cast... as date" ein Problem darstellen, da in solchen Fällen auf der Datenbank kein Index verwendet werden kann.
Zu prüfen ist also, warum der Cast erforderlich ist bzw. warum der Parameter nicht dem Datenbankfeld entsprechend übergeben wird.
Dann ließe sich ein Index erstellen, der die Performance u.U. drastisch erhöht.Wie werden denn die SQL's aufgerufen?
Liegt da VBA-Code dahinter?
Sind die "?"-Parameter ggf. Zellen zugeordnet?
Auf welche Datenbank wird hier zugegriffen? -
Nein bei jedem Feld wird nur eine dieser Berechnungen ausgeführt.
Dahinter liegt VBA-Code, am Ende dieser Antwort werde ich einmal den Code von einer der abfragen mitgeben, vielleicht hilft dies ja weiter.
Ob die "?"-Parameter einer speziellen Zelle zugeordnet sind, kann ich leider nicht sagen, dies müsste ich selbst nachprüfen lassen.
Zugegriffen wird auf eine Firebird-Datenbank.
Hier der Code der zweiten SQL-Abfrage:
Public Function NumRein(aAviseDate As Date, aZielland As String) As Integer ' Anzahl avisierter Sendungen nach Datum und Land ' ' Abgegrenzt wird nach Tag und Uhrzeit, wobei für jedes Land ein anderer Zeitpunkt verwendet wird, so daß nach Tagesabschluß ' avisierte Sendungen zum folgenden Arbeitstag gezählt werden. Fällt der vorangehende Arbeitstag auf ein Wochenende, wird ab ' Freitag nach Tagesabschluß gezählt. ' 'declare ADO Connection, Command, Parameter and Recordset Dim cnDB As New ADODB.Connection Dim Cm As New ADODB.Command Dim Pm1, Pm2 As ADODB.Parameter Dim rsRecords As ADODB.Recordset 'Open the ODBC Connection cnDB.Open ("HVS32 live") 'Command for select Cm.ActiveConnection = cnDB Cm.CommandText = "select count(*) from ((select lieferungnr from lieferung " & _ "where speicherdatetime between ? and ?) as lf " & _ "join teillieferung tl on lf.lieferungnr = tl.lieferungnr " & _ "join versandeinheit ve on tl.versandeinheitnr = ve.versandeinheitnr " & _ "join abrechnungseinheit ae on ve.abrechnungseinheitnr = ae.abrechnungseinheitnr) " & _ "where zieladrlkz = ? and vestatus is null and labelname <> 'NeutralSpedition'" Cm.CommandType = adCmdText 'use parameter objects to deal with data types Select Case aZielland Case Is = "DE" dtFrom = DateValue(WDay(aAviseDate, -1)) + TimeValue("16:00") dtUntil = DateValue(aAviseDate) + TimeValue("16:00") Case Is = "AT" dtFrom = DateValue(WDay(aAviseDate, -1)) + TimeValue("15:00") dtUntil = DateValue(aAviseDate) + TimeValue("15:00") Case Is = "CH" dtFrom = DateValue(WDay(aAviseDate, -1)) + TimeValue("14:00") dtUntil = DateValue(aAviseDate) + TimeValue("14:00") Case Is = "NL" dtFrom = DateValue(WDay(aAviseDate, -1)) + TimeValue("13:00") dtUntil = DateValue(aAviseDate) + TimeValue("13:00") Case Is = "FR" dtFrom = DateValue(WDay(aAviseDate, -1)) + TimeValue("12:00") dtUntil = DateValue(aAviseDate) + TimeValue("12:00") Case Is = "GB" dtFrom = DateValue(WDay(aAviseDate, -1)) + TimeValue("11:00") dtUntil = DateValue(aAviseDate) + TimeValue("11:00") Case Else dtFrom = DateValue(WDay(aAviseDate, -1)) + TimeValue("16:00") dtUntil = DateValue(aAviseDate) + TimeValue("16:00") End Select Cm.Parameters.Append Cm.CreateParameter("Avise", adDBTimeStamp, adParamInput, 0, dtFrom) Cm.Parameters.Append Cm.CreateParameter("Avise", adDBTimeStamp, adParamInput, 0, dtUntil) Cm.Parameters.Append Cm.CreateParameter("Land", adVarChar, adParamInput, 3, aZielland) 'Open the command with the Recordset Set rsRecords = Cm.Execute 'Temp - Result Dim Result As Integer 'read field value If Not IsNull(rsRecords.Fields.Item(0)) Then Result = rsRecords.Fields.Item(0) Else Result = 0 End If 'close all rsRecords.Close cnDB.Close 'Set the return value of the function (same name) NumRein = Result End Function
- Bearbeitet ShinMaes Montag, 6. November 2017 09:09
-
Dazu müsste man nun die Definition der betroffenen Tabellen der Firebird kennen um den SQL zu optimieren als auch um ggf. Indizes anzulegen als auch den VBA code anzupassen.
Normalerweise bedarf es da allein schon keiner länderspezifischen Behandlung.
Da ich nun VBA als auch Firebird schon sehr lange betreibe (Firebird ca. 12 Jahre, VB/VBA ca. 20 Jahre) könnte ich dir da auch helfen.
Allerdings lebe ich auch von dieser Tätigkeit (€).
Im Rahmen dieses Forums ist der Aufwand und eben auch die Raterei einfach zu groß um wirklich hier konkret helfen zu können.
Bei Interesse: Finden kannst du mich mit "Baldur Fürchau";-). -
Eine letzte Sache ist mir dazu eingefallen.
Bei einem Test auf einer Windows 7 Maschine (selbe Office-Version) ist diese Abfrage in weitaus kürzerer Zeit durchgelaufen.
Sollte diese Information nicht weiterhelfen, lassen wir das Thema halt einfach so stehen.
-
Beide PCs greifen auf die selbe Datenbank zu.
Den Sweeper über die Datenbank laufen zu lassen klingt ja sinnig, aber würde in einem hieraus resultierenden Fehlerfall nicht auf BEIDEN Maschine diese extrem lange Laufzeit auftreten? Es kann doch nicht sein, dass die Performance auf dem Windows 10 Client so viel schlechter ist.
-
Da gebe ich dir allerdings recht.
Das kann dann nichts mehr mit den SQL's und der Datenbank zu tun haben.
Es muss also irgendwo am Netzwerk liegen.Was allerdings noch sein könnte wäre die Einstellung des Connection-Pooling in ODBC.
Wenn diese fehlt, wird jedesmal die Verbindung neu geöffnet und anschließend geschlossen.
Ist das Connection-Pooling eingeschaltet, entfällt der reale Close und beim nächsten Open wird die Verbindung wieder verwendet. Dies könnte enorm Zeit sparen.
Wie man dies ggf. einschaltet kannst du vielleicht hier entnehmen:
https://docs.microsoft.com/en-us/sql/odbc/admin/setting-odbc-connection-pooling-optionsVergleiche mal genau sämtliche Einstellungen der ODBC-Verbindung "HVS32 live", wichtig ODBC-Verwaltung 32-Bit.
-
-
Wenn Sie sich ein wenig mit VBA auskennen, so können Sie eine kleine Performanceprüfung durchführen:
dim StartTime as Double
StartTime = Timer
Aktion durchführen
Debug.Print "Aktion: "; Timer-StartTimeSo könnte man die einzelnen Schritte genau prüfen und die Unterschiede zwischen den PC's ermitteln.
Beispiel:
StartTime = Timer
cnDB.Open
Debug.Print "Open: ";Timer-StartTimeDie Ausgabe wird im Direktfenster gemacht (Ansicht->Direktfenster, bzw. ggf. STRG+G).
Die Funktion Timer liefert die Uhrzeit in Sekunden seit Mitternacht zzgl. 2 Nachkommastellen, also hier ausreichende Genauigkeit.- Bearbeitet Der Suchende Donnerstag, 9. November 2017 09:35
-
Hallo nochmal,
ich habe den angegebenen Code einmal eingefügt. Genauer gesagt habe ich die Zeit einmal vor dem cnDB.Open und einmal nach den Abfragen die Zeit ausgeben lassen.
Dabei zeigte sich eine für mich doch recht interessante Begebenheit:
Die Zeiten zum Ende lagen alle im bereich zwischen 0,5 und 3 Sekunden (ich vermute einfach mal, dass die Zeit in Sekunden angegeben wird).
Weiterhin kam es mehrfach vor, dass die Ausgabe vom Anfang ausgegeben wird, dann geschieht einige Zeit gar nicht und die Zeit am Ende beträgt dann nur 0,xx Sekunden, das kann ja eigentlich schon nicht korrekt sein.
Für mich sieht das so aus, als würde der komplette Timer einfach stehen bleiben.
Worauf ich vorher gar nicht geachtet hatte, was mir aber jetzt auffiel:
Sinn hinter den Abfragen ist es ja, die Felder einer Excel-Tabelle zu füllen. Allerdings werden diese Felder teilweise erst nach 3-4 Durchläufen der Berechnung gefüllt, davor passiert einfach nichts.
Gestartet habe ich die Berechnungen über jede mir erdenkliche Methode, die Excel mir bietet.Für mich sieht es hier immer weniger nach einem Fehler mit der Datenbankabfrage aus, als viel mehr um eine Inkompatibilität von Excel mit irgendeinem Teil des Codes.
Hilft es vielleicht bei der Lösungsfindung, dass die Datei schon etwas älter ist und frühestens unter Excel 2007 erstellt wurde, könnte sich hier einfach etwas an dem Verhalten von Excel geändert haben?
-
Hier übrigens ein Teil der Ergebnisse:
NumRein Anfang: 0
NumRein Ende: 0,40625
NumRaus Anfang: 0
NumRaus Ende: 27,564453125
SDD Anfang: 0
SDD Ende: 0,828125
NumRein Anfang: 0
NumRein Ende: 0,421875
NumRaus Anfang: 0
NumRaus Ende: 0,59375
NumRaus Anfang: 0
NumRaus Ende: 3,802734375
SDD Anfang: 0
SDD Ende: 0,36328125
SDD Anfang: 0
SDD Ende: 0,34375
SDD Anfang: 0
SDD Ende: 0,763671875
SDD Anfang: 0
SDD Ende: 0,37109375
NumRein Anfang: 0
NumRein Ende: 0,470703125
SDD Anfang: 0
SDD Ende: 0,31640625
SDD Anfang: 0
SDD Ende: 0,46484375
NumRein Anfang: 0
NumRein Ende: 0,392578125
NumRaus Anfang: 0
NumRaus Ende: 1,328125
SDD Anfang: 0
SDD Ende: 0,34375
SDD Anfang: 0
SDD Ende: 0,328125
SDD Anfang: 0
SDD Ende: 0,32421875
SDD Anfang: 0
SDD Ende: 0,880859375
SDD Anfang: 0
SDD Ende: 1,068359375 -
An der Kompatibilität liegt es nicht.
VBA's, die ich mit Excel 97 entwickelt habe, laufen auch mit Excel 2016 ohne Veränderung!Interessant ist da schon, dass eine Aufgabe 27 Sekunden dauert.
Da ich deinen Code nicht erraten kann, kann ich eben nur vermuten.
Sicher ist auf jeden Fall, dass alle ADO-Aktionen synchron laufen und somit während dieser Zeit tatsächlich keine Reaktion von Excel möglich ist (ggf. Anzeige im Header "Anwendung reagiert nicht").Als nächstes enthält die Routine keine Fehlerbehandlung, so dass diese über zentrale Einstellungen ignoriert werden. Dies führt u.U. zu keinem Ergebnis, da die Funktion auf Grund Fehler abgebrochen wurde.
Alles nur Spekulation. Da du ja nicht fit in VBA bist (wie du sagtest), solltest du dir da von einem Spezialisten helfen lassen. Es macht ansonsten keinen Sinn.
"Nichts ist ömsönst".