Ich habe einen Algorithmus, den ich für jede Zeile in einer Tabelle mit 800 KB Zeilen und 38 Spalten ausführen muss. Der Algorithmus ist in VBA implementiert und führt eine Reihe von Berechnungen mit Werten aus einigen Spalten durch, um andere Spalten zu bearbeiten.
Ich verwende derzeit Excel (ADO), um SQL abzufragen, und verwende VBA mit clientseitigen Cursorn, um den Algorithmus per Schleife durch jede Zeile anzuwenden. Es funktioniert, dauert aber 7 Stunden.
Der VBA-Code ist so komplex, dass es eine Menge Arbeit wäre, ihn in T-SQL umzukodieren.
Ich habe über CLR-Integration und UDFs als mögliche Routen gelesen. Ich habe auch darüber nachgedacht, den VBA-Code in eine SSIS-Skriptaufgabe einzufügen, um näher an die Datenbank heranzukommen, bin mir jedoch sicher, dass es eine Expertenmethode für diese Art von Leistungsproblem gibt.
Idealerweise könnte ich den Algorithmus für so viele Zeilen (alle?) Wie möglich auf der Basis von parallelen Mengen ausführen.
Jede Hilfe basiert auf der Frage, wie mit dieser Art von Problem die beste Leistung erzielt werden kann.
--Bearbeiten
Vielen Dank für die Kommentare, ich verwende MS SQL 2014 Enterprise, hier sind einige weitere Details:
Der Algorithmus findet charakteristische Muster in Zeitreihendaten. Die Funktionen innerhalb des Algorithmus führen eine polynomielle Glättung und Fensterung durch und finden interessierende Bereiche basierend auf Eingabekriterien, wobei ein Dutzend Werte und einige boolesche Ergebnisse zurückgegeben werden.
Meine Frage bezieht sich eher auf die Methodik als auf den eigentlichen Algorithmus: Welche Optionen stehen mir zur Verfügung, wenn ich mehrere Zeilen gleichzeitig parallel berechnen möchte.
Ich sehe, dass das Umcodieren in T-SQL empfohlen wird, was eine Menge Arbeit ist, aber möglich, jedoch arbeitet der Algorithmusentwickler in VBA und es ändert sich häufig, so dass ich mit der T-SQL-Version synchron bleiben und jeden erneut validieren muss Veränderung.
Ist T-SQL die einzige Möglichkeit, setbasierte Funktionen zu implementieren?
quelle
N
undN
Instanzen Ihres Algorithmus aufN
separaten Prozessoren / Computern ausführen . Auf der anderen Seite, was ist Ihr Hauptengpass - die Übertragung der Daten von SQL Server nach Excel oder tatsächliche Berechnungen? Wie lange würde der gesamte Vorgang dauern, wenn Sie die VBA-Funktion so ändern, dass sofort ein Dummy-Ergebnis zurückgegeben wird? Wenn es noch Stunden dauert, liegt ein Engpass in der Datenübertragung vor. Wenn es Sekunden dauert, müssen Sie den VBA-Code für die Berechnungen optimieren.SELECT AVG([AD_Sensor_Data]) OVER (ORDER BY [RowID] ROWS BETWEEN 5 PRECEDING AND 5 FOLLOWING) as 'AD_Sensor_Data' FROM [AD_Points] WHERE [FileID] = @FileID ORDER BY [RowID] ASC
Im Management Studio diese Funktion , die 50 ms für jede der Zeilen aufgerufen wird , nimmt(FileID, RowID)
.Antworten:
In Bezug auf die Methodik glaube ich, dass Sie den falschen B-Baum bellen ;-).
Was wir wissen:
Lassen Sie uns zunächst konsolidieren und überprüfen, was wir über die Situation wissen:
Es gibt eine gespeicherte Prozedur, die für jede Zeile aufgerufen wird:
Die Definition ist (zumindest teilweise):
Was wir vermuten können:
Als Nächstes können wir uns alle diese Datenpunkte zusammen ansehen, um herauszufinden, ob wir zusätzliche Details synthetisieren können, die uns helfen, einen oder mehrere Engpässe zu finden, und entweder auf eine Lösung hinweisen oder zumindest einige mögliche Lösungen ausschließen.
Die derzeitige Denkrichtung in den Kommentaren ist, dass das Hauptproblem die Datenübertragung zwischen SQL Server und Excel ist. Ist das wirklich der Fall? Wenn die gespeicherte Prozedur für jede der 800.000 Zeilen aufgerufen wird und für jeden Aufruf (dh für jede Zeile) 50 ms benötigt, summiert sich dies zu 40.000 Sekunden (nicht ms). Und das entspricht 666 Minuten (hhmm ;-) oder etwas mehr als 11 Stunden. Es wurde jedoch gesagt, dass der gesamte Prozess nur 7 Stunden in Anspruch nimmt. Wir sind bereits 4 Stunden über die gesamte Zeit, und wir haben sogar rechtzeitig hinzugefügt, um die Berechnungen durchzuführen oder die Ergebnisse wieder in SQL Server zu speichern. Also stimmt hier etwas nicht.
Bei der Definition der gespeicherten Prozedur gibt es nur einen Eingabeparameter für
@FileID
; Es ist kein Filter aktiviert@RowID
. Ich vermute also, dass eines der folgenden beiden Szenarien eintritt:@FileID
, die sich anscheinend über ungefähr 4000 Zeilen erstreckt. Wenn die angegebenen 4000 zurückgegebenen Zeilen eine ziemlich konsistente Menge sind, gibt es nur 200 dieser Gruppen in den 800.000 Zeilen. Und 200 Ausführungen, die jeweils 50 ms dauern, sind in diesen 7 Stunden nur 10 Sekunden.@FileID
etwas länger, bis beim ersten Übergeben einer neuen Zeile neue Zeilen in den Pufferpool gezogen werden. Die nächsten 3999 Ausführungen werden jedoch in der Regel schneller zurückgegeben, da sie bereits vorhanden sind zwischengespeichert, richtig?Ich denke, dass die Konzentration auf diese gespeicherte Prozedur "Filter" oder jede Datenübertragung von SQL Server nach Excel ein roter Faden ist .
Im Moment sind meines Erachtens die wichtigsten Indikatoren für eine schwache Leistung:
Ich vermute, dass:
UPDATE
Kontoauszüge auszustellen , das sind 800.000 separate Transaktionen.Meine Empfehlung (basierend auf aktuell verfügbaren Informationen):
Ihr größter Verbesserungsbereich besteht darin, mehrere Zeilen gleichzeitig zu aktualisieren (dh in einer Transaktion). Sie sollten Ihren Prozess Arbeit in Bezug auf jede Aktualisierung
FileID
statt jedemRowID
. So:FileID
in ein ArrayFileID
) berechnet wurden:RowID
Wenn Ihr Clustered-Index noch nicht als definiert
(FileID, RowID)
ist, sollten Sie dies berücksichtigen (wie @MikaelEriksson in einem Kommentar zur Frage vorgeschlagen hat). Es hilft diesen Singleton-UPDATEs nicht, aber es würde die Aggregatoperationen zumindest geringfügig verbessern, z. B. was Sie in dieser gespeicherten "Filter" -Prozedur tun, da sie alle auf basierenFileID
.Sie sollten erwägen, die Logik in eine kompilierte Sprache zu verschieben. Ich würde vorschlagen, eine .NET WinForms-App oder sogar eine Konsolen-App zu erstellen. Ich bevorzuge die Konsolen-App, da das Planen über SQL Agent oder Windows Scheduled Tasks einfach ist. Es sollte egal sein, ob es in VB.NET oder C # gemacht wird. VB.NET passt möglicherweise besser zu Ihrem Entwickler, es wird jedoch noch eine gewisse Lernkurve geben.
Ich sehe derzeit keinen Grund, zu SQLCLR zu wechseln. Wenn sich der Algorithmus häufig ändert, wird es ärgerlich, die Assembly die ganze Zeit neu bereitzustellen. Das erneute Erstellen einer Konsolen-App und das Ablegen der EXE-Datei im richtigen freigegebenen Ordner im Netzwerk, sodass Sie nur dasselbe Programm ausführen und es zufällig immer auf dem neuesten Stand ist, sollte relativ einfach sein.
Ich denke nicht, dass es hilfreich wäre, die Verarbeitung vollständig in T-SQL zu verlagern, wenn das Problem meines Erachtens darin besteht, dass Sie jeweils nur ein UPDATE ausführen.
Wenn die Verarbeitung in .NET verschoben wird, können Sie dann TVPs (Table-Valued Parameters) verwenden, sodass Sie das Array an eine gespeicherte Prozedur übergeben, die eine
UPDATE
JOIN-Anweisung für die TVP-Tabellenvariable aufruft und somit eine einzelne Transaktion ist . Der TVP sollte schneller sein als 4000INSERT
s, die in einer einzigen Transaktion zusammengefasst sind. Der Gewinn durch die Verwendung von TVPs mit mehr als 4000INSERT
Sekunden in einer Transaktion ist jedoch wahrscheinlich nicht so hoch wie die Verbesserung, die beim Übergang von 800.000 separaten Transaktionen auf nur 200 Transaktionen mit jeweils 4000 Zeilen zu verzeichnen ist.Die TVP-Option ist für die VBA-Seite nicht von Haus aus verfügbar, aber jemand hat sich eine Lösung ausgedacht, die möglicherweise einen Test wert ist:
Wie kann ich die Datenbankleistung beim Wechsel von VBA zu SQL Server 2008 R2 verbessern?
WENN der Filter-Proc nur
FileID
in derWHERE
Klausel verwendet wird und wenn dieser Proc wirklich für jede Zeile aufgerufen wird, können Sie Verarbeitungszeit sparen, indem Sie die Ergebnisse des ersten Laufs zwischenspeichern und für die restlichen Zeilen verwendenFileID
. richtig?Sobald Sie die Verarbeitung getan pro FileID , dann können wir reden über eine parallele Verarbeitung beginnen. Aber das ist zu diesem Zeitpunkt vielleicht nicht nötig :). Angesichts der Tatsache, dass es sich um drei nicht ideale Hauptteile handelt: Excel-, VBA- und 800k-Transaktionen, ist jede Rede von SSIS oder Parallelogrammen oder wer weiß was, vorzeitige Optimierung / vor dem Pferd liegendes Zeug . Wenn wir diesen 7-stündigen Prozess auf 10 Minuten oder weniger reduzieren können, würden Sie dann noch über zusätzliche Möglichkeiten nachdenken, um ihn schneller zu machen? Gibt es eine Zielerfüllungszeit, die Sie im Auge haben? Denken Sie daran, dass die Verarbeitung einmal pro FileID erfolgt Wenn Sie eine VB.NET-Konsolenanwendung (dh eine Befehlszeilen-EXE-Datei) hätten, würde Sie nichts daran hindern, einige dieser Datei-IDs gleichzeitig auszuführen :), sei es über den SQL Agent-CmdExec-Schritt oder über Windows Scheduled Tasks. etc.
UND Sie können immer einen "schrittweisen" Ansatz wählen und gleichzeitig einige Verbesserungen vornehmen. Beginnen Sie beispielsweise mit den Aktualisierungen pro
FileID
und verwenden Sie daher eine Transaktion für diese Gruppe. Versuchen Sie dann, den TVP zum Laufen zu bringen. Dann lesen Sie, wie Sie diesen Code nehmen und nach VB.NET verschieben (und TVPs funktionieren in .NET, so dass eine gute Portierung möglich ist).Was wir nicht wissen, das könnte noch helfen:
UPDATE 1:
** Es scheint einige Verwirrung darüber zu geben, was VBA (Visual Basic für Applikationen) und was damit gemacht werden kann. Dies soll nur sicherstellen, dass wir uns alle auf derselben Webseite befinden:
UPDATE 2:
Ein weiterer zu berücksichtigender Punkt: Wie werden Verbindungen behandelt? Öffnet und schließt der VBA-Code die Verbindung für jeden Vorgang oder öffnet er die Verbindung zu Beginn des Prozesses und schließt sie am Ende des Prozesses (dh 7 Stunden später)? Selbst mit dem Verbindungspooling (das standardmäßig für ADO aktiviert sein sollte) sollte es immer noch erhebliche Auswirkungen zwischen einmaligem Öffnen und Schließen geben, anstatt 800.200- oder 1.600.000-maliges Öffnen und Schließen. Diese Werte basieren auf mindestens 800.000 UPDATEs plus entweder 200 oder 800.000 EXECs (abhängig davon, wie oft die gespeicherte Filterprozedur tatsächlich ausgeführt wird).
Dieses Problem mit zu vielen Verbindungen wird durch die oben beschriebene Empfehlung automatisch gemildert. Indem Sie eine Transaktion erstellen und alle UPDATES innerhalb dieser Transaktion ausführen, halten Sie diese Verbindung offen und verwenden sie für jede Transaktion erneut
UPDATE
. Ob die Verbindung vom ersten Aufruf an offen gehalten wird, um die 4000 Zeilen pro angegebenemFileID
Wert abzurufen, oder nach diesem Vorgang "get" geschlossen und erneut für die UPDATEs geöffnet wird, hat weitaus weniger Auswirkungen, da wir jetzt über einen Unterschied von beidem sprechen Insgesamt 200 oder 400 Verbindungen über den gesamten Prozess.UPDATE 3:
Ich habe ein paar schnelle Tests gemacht. Bitte beachten Sie, dass dies ein eher kleiner Test ist und nicht genau dieselbe Operation (pure INSERT vs EXEC + UPDATE). Die zeitlichen Unterschiede in Bezug auf den Umgang mit Verbindungen und Transaktionen sind jedoch nach wie vor relevant, sodass die Informationen so hochgerechnet werden können, dass sie hier einen relativ ähnlichen Einfluss haben.
Testparameter:
Tabelle:
Betrieb:
TRUNCATE TABLE dbo.ManyInserts;
(Angesichts der Art dieses Tests schienen FREEPROCCACHE, FREESYSTEMCACHE und DROPCLEANBUFFERS keinen großen Mehrwert zu bieten.)Ergebnisse:
Selbst wenn die ADO-Verbindung zur Datenbank bereits für alle Vorgänge freigegeben ist, wird die Gruppierung in Batches mithilfe einer expliziten Transaktion (das ADO-Objekt sollte in der Lage sein, dies zu handhaben) mit Sicherheit erheblich verbessert (dh um mehr als das Doppelte). Reduzieren Sie die Gesamtprozesszeit.
quelle
IMHO und unter der Annahme, dass es nicht möglich ist, das VBA-Sub in SQL umzucodieren, haben Sie in Betracht gezogen, dem VBA-Skript zu erlauben, die Auswertung in der Excel-Datei abzuschließen und die Ergebnisse dann über SSIS an den SQL-Server zurückzuschreiben?
Sie können festlegen, dass das VBA-Sub mit dem Spiegeln eines Indikators entweder in einem Dateisystemobjekt oder auf dem Server beginnt und endet (wenn Sie die Verbindung so konfiguriert haben, dass sie auf den Server zurückschreibt), und dann diesen Indikator mit einem SSIS-Ausdruck auf das Symbol prüfen
disable
Eigenschaft einer bestimmten Aufgabe in Ihrer SSIS-Lösung (sodass der Importvorgang wartet, bis der VBA-Sub abgeschlossen ist, wenn Sie befürchten, dass der Zeitplan überschritten wird).Außerdem könnte das VBA-Skript programmgesteuert gestartet werden (ein bisschen wackelig, aber ich habe die
workbook_open()
Eigenschaft verwendet, um in der Vergangenheit Aufgaben dieser Art auszulösen, die das Auslösen und Vergessen betreffen ).Wenn die Evaluierungszeit des VB-Skripts zu einem Problem wird, können Sie feststellen, ob Ihr VB-Entwickler bereit und in der Lage ist, seinen Code in eine VB-Skriptaufgabe innerhalb der SSIS-Lösung zu portieren Arbeiten mit Daten auf diesem Datenträger.
quelle