Mam algorytm, który muszę uruchomić dla każdego wiersza w tabeli z 800 000 wierszy i 38 kolumnami. Algorytm jest zaimplementowany w języku VBA i wykonuje kilka obliczeń matematycznych przy użyciu wartości z niektórych kolumn do manipulowania innymi kolumnami.
Obecnie używam Excela (ADO) do zapytania SQL i używam VBA z kursorami po stronie klienta, aby zastosować algorytm przez pętlę przez każdy wiersz. Działa, ale uruchomienie zajmuje 7 godzin.
Kod VBA jest na tyle skomplikowany, że przekodowanie go do T-SQL byłoby bardzo pracochłonne.
Przeczytałem o integracji CLR i UDF jako możliwych trasach. Zastanawiałem się także nad umieszczeniem kodu VBA w zadaniu skryptu SSIS, aby zbliżyć się do bazy danych, ale jestem pewien, że istnieje specjalistyczna metodologia tego typu problemów z wydajnością.
Idealnie byłbym w stanie uruchomić algorytm na jak największej liczbie wierszy (wszystkich?) W sposób oparty na zestawie równoległym.
Każda pomoc w znacznym stopniu zależy od tego, jak uzyskać najlepszą wydajność przy tego rodzaju problemach.
--Edytować
Dzięki za komentarze, używam MS SQL 2014 Enterprise, oto kilka szczegółów:
Algorytm znajduje charakterystyczne wzorce w danych szeregów czasowych. Funkcje w ramach algorytmu wykonują wygładzanie wielomianowe, okienkowanie i wyszukują obszary zainteresowania na podstawie kryteriów wejściowych, zwracając tuzin wartości i niektóre wyniki logiczne.
Moje pytanie dotyczy bardziej metodologii niż faktycznego algorytmu: jeśli chcę osiągnąć równoległe obliczenia w wielu wierszach jednocześnie, jakie są moje opcje.
Widzę, że zalecane jest ponowne kodowanie do T-SQL, co jest bardzo pracochłonne, ale możliwe, jednak programista algorytmu działa w VBA i często się zmienia, więc musiałbym być zsynchronizowany z wersją T-SQL i sprawdzać ponownie co zmiana.
Czy T-SQL jest jedynym sposobem na wdrożenie funkcji opartych na zestawie?
źródło
N
partie i uruchomićN
wystąpienia algorytmu naN
osobnych procesorach / komputerach. Z drugiej strony, jakie jest Twoje główne wąskie gardło - przesyłanie danych z SQL Server do Excela lub rzeczywistych obliczeń? Jeśli zmienisz funkcję VBA, aby natychmiast zwracać jakiś fałszywy wynik, ile czasu zajmie cały proces? Jeśli nadal zajmuje to godziny, wąskie gardło jest w przesyłaniu danych. Jeśli zajmie to kilka sekund, musisz zoptymalizować kod VBA, który wykonuje obliczenia.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
Management Studio ta funkcja, która jest wywoływana dla każdego wiersza, zajmuje 50 ms(FileID, RowID)
.Odpowiedzi:
Jeśli chodzi o metodologię, wydaje mi się, że szczekasz na niewłaściwe drzewko ;-).
Co wiemy:
Najpierw skonsolidujmy i sprawdźmy, co wiemy o sytuacji:
Dla każdego wiersza wywoływana jest procedura składowana:
Definicja (przynajmniej częściowo) to:
Co możemy założyć:
Następnie możemy spojrzeć na wszystkie te punkty danych razem, aby zobaczyć, czy możemy zsyntetyzować dodatkowe szczegóły, które pomogą nam znaleźć jedno lub więcej szyjek butelek, i albo wskażemy rozwiązanie, albo przynajmniej wykluczymy niektóre możliwe rozwiązania.
Obecny kierunek myślenia w komentarzach jest taki, że głównym problemem jest transfer danych między SQL Server i Excel. Czy to naprawdę tak jest? Jeśli procedura składowana jest wywoływana dla każdego z 800 000 wierszy i zajmuje 50 ms na każde wywołanie (tj. Dla każdego wiersza), daje to 40 000 sekund (nie ms). A to odpowiada 666 minutom (hhmm ;-), czyli nieco ponad 11 godzin. Jednak cały proces miał zająć tylko 7 godzin. Łącznie mamy już 4 godziny, a nawet dodaliśmy czas na wykonanie obliczeń lub zapisanie wyników z powrotem na SQL Server. Więc czegoś tu nie ma.
Patrząc na definicję procedury składowanej, istnieje tylko parametr wejściowy dla
@FileID
; nie ma żadnego filtra@RowID
. Podejrzewam więc, że dzieje się jeden z następujących dwóch scenariuszy:@FileID
, który wydaje się obejmować około 4000 wierszy. Jeśli podane 4000 wierszy zwróconych jest dość spójną kwotą, to tylko 200 z nich grupuje się w 800 000 wierszy. A 200 egzekucji po 50 ms to tylko 10 sekund z 7 godzin.@FileID
przekazaniu nowego zajmie trochę więcej czasu, aby pobrać nowe wiersze do puli buforów, ale wtedy kolejne 3999 wykonań zwykle powróci szybciej, ponieważ są już w pamięci podręcznej, prawda?Myślę, że skupienie się na tej procedurze przechowywanej „filtru” lub jakimkolwiek transferze danych z SQL Server do Excela to czerwony śledź .
W tej chwili uważam, że najbardziej odpowiednie wskaźniki słabych wyników to:
Podejrzewam, że:
UPDATE
wyciągów, co stanowi 800 000 oddzielnych transakcji.Moja rekomendacja (na podstawie obecnie dostępnych informacji):
Twoim największym ulepszeniem byłoby zaktualizowanie wielu wierszy jednocześnie (tj. W jednej transakcji). Powinieneś zaktualizować swój proces, aby działał pod względem każdego
FileID
zamiast każdegoRowID
. Więc:FileID
do tablicyFileID
obliczeniu wszystkich wierszy w tablicy (tj. dla tego konkretnego ):RowID
Jeśli indeks klastrowany nie jest jeszcze zdefiniowany jako
(FileID, RowID)
, należy to rozważyć (jak sugerował @MikaelEriksson w komentarzu do pytania). Nie pomoże tym pojedynczym AKTUALIZACJOM, ale przynajmniej nieznacznie poprawi operacje agregujące, takie jak to, co robisz w tej „filtrowanej” procedurze przechowywanej, ponieważ wszystkie są oparteFileID
.Powinieneś rozważyć przeniesienie logiki do skompilowanego języka. Sugerowałbym utworzenie aplikacji .NET WinForms lub nawet aplikacji konsolowej. Wolę aplikację konsolową, ponieważ można ją łatwo zaplanować za pomocą agenta SQL lub zaplanowanych zadań systemu Windows. Nie powinno mieć znaczenia, czy odbywa się to w VB.NET, czy w C #. VB.NET może być bardziej naturalny dla twojego programisty, ale nadal będzie trochę krzywej uczenia się.
W tej chwili nie widzę powodu, aby przejść do SQLCLR. Jeśli algorytm zmienia się często, denerwujące byłoby zmuszanie do ponownego wdrażania zestawu przez cały czas. Odbudowanie aplikacji konsoli i umieszczenie pliku .exe w odpowiednim folderze współdzielonym w sieci, tak aby po prostu uruchomić ten sam program i zdarza się, że zawsze jest aktualny, powinno być dość łatwe.
Nie sądzę, aby przeniesienie przetwarzania w pełni do T-SQL pomogłoby, jeśli podejrzewam, że problem jest taki, a ty wykonujesz jedną aktualizację na raz.
Jeśli przetwarzanie zostanie przeniesione do platformy .NET, można następnie użyć parametrów wycenionych w tabeli (TVP), aby przekazać tablicę do procedury
UPDATE
składowanej, która wywołałaby wywołanie JOIN do zmiennej tabeli TVP, a zatem jest pojedynczą transakcją . TVP powinien być szybszy niż 4000INSERT
pogrupowane w jedną transakcję. Ale zysk wynikający z używania TVP przez ponad 4000INSERT
s w 1 transakcji prawdopodobnie nie będzie tak znaczący, jak poprawa widoczna przy przejściu z 800 000 oddzielnych transakcji do tylko 200 transakcji po 4000 wierszy każda.Opcja TVP nie jest natywnie dostępna dla strony VBA, ale ktoś wymyślił obejście, które może być warte przetestowania:
Jak poprawić wydajność bazy danych, przechodząc z VBA do SQL Server 2008 R2?
JEŚLI filtr proc używa tylko
FileID
wWHERE
klauzuli i JEŻELI ten proc jest tak naprawdę wywoływany dla każdego wiersza, możesz zaoszczędzić trochę czasu przetwarzania, buforując wyniki pierwszego uruchomienia i wykorzystując je dla pozostałych wierszyFileID
, dobrze?Po uzyskaniu przetwarzanie odbywa się za fileid , wtedy możemy zacząć mówić o przetwarzaniu równoległym. Ale w tym momencie może to nie być konieczne :). Biorąc pod uwagę, że masz do czynienia z 3 dość dużymi, nie idealnymi częściami: transakcjami Excel, VBA i 800 tys., Każda rozmowa o SSIS lub równoległoboki lub kto-co-wie, to przedwczesna optymalizacja / typ wózka przed koniem . Jeśli uda nam się zmniejszyć ten 7-godzinny proces do 10 minut lub krócej, czy nadal zastanawiasz się nad dodatkowymi sposobami na przyspieszenie? Czy masz na myśli docelowy czas realizacji? Należy pamiętać, że po zakończeniu przetwarzania dla jednego identyfikatora pliku podstawa: jeśli masz aplikację konsoli VB.NET (tj. wiersza polecenia .EXE), nic nie powstrzyma cię przed uruchomieniem kilku tych identyfikatorów plików na raz :), czy to za pośrednictwem kroku CmdExec agenta SQL, czy Zaplanowanych zadań systemu Windows, itp.
I zawsze możesz zastosować podejście „etapowe” i wprowadzić kilka ulepszeń naraz. Na przykład, począwszy od robienia aktualizacji dla,
FileID
a więc przy użyciu jednej transakcji dla tej grupy. Następnie sprawdź, czy możesz uruchomić TVP. Następnie zapoznaj się z pobieraniem tego kodu i przenoszeniem go do VB.NET (a TVP działają w .NET, więc ładnie się ładuje).Czego nie wiemy, co może pomóc:
AKTUALIZACJA 1:
** Wydaje się, że istnieje pewne zamieszanie dotyczące tego, co VBA (Visual Basic for Applications) i co można z nim zrobić, więc to po prostu upewnienie się, że wszyscy jesteśmy na tej samej stronie internetowej:
AKTUALIZACJA 2:
Jeszcze jedna rzecz do rozważenia: w jaki sposób obsługiwane są połączenia? Czy kod VBA otwiera i zamyka połączenie dla każdej operacji, czy też otwiera połączenie na początku procesu i zamyka pod koniec procesu (tj. 7 godzin później)? Nawet przy pulowaniu połączeń (które domyślnie powinny być włączone dla ADO), nadal powinno istnieć spory wpływ na jednokrotne otwarcie i zamknięcie w przeciwieństwie do otwierania i zamykania 800, 200 lub 1 600 000 razy. Wartości te opierają się na co najmniej 800 000 AKTUALIZACJACH plus 200 lub 800k EXEC (w zależności od tego, jak często faktycznie wykonywana jest procedura przechowywana filtru).
Problem zbyt wielu połączeń jest automatycznie łagodzony przez zalecenie, które przedstawiłem powyżej. Tworząc transakcję i wykonując wszystkie AKTUALIZACJE w ramach tej transakcji, utrzymasz to połączenie otwarte i ponownie wykorzystasz je dla każdej z nich
UPDATE
. To, czy połączenie jest utrzymywane otwarte od pierwszego wywołania, aby uzyskać 4000 wierszy dla określonegoFileID
, czy zamknięte po tej operacji „get” i ponownie otwarte dla aktualizacji, ma o wiele mniejszy wpływ, ponieważ teraz mówimy o różnicy Łącznie 200 lub 400 połączeń w całym procesie.AKTUALIZACJA 3:
Zrobiłem kilka szybkich testów. Należy pamiętać, że jest to test na małą skalę, a nie dokładnie ta sama operacja (czysty INSERT vs EXEC + UPDATE). Różnice w czasie związane ze sposobem obsługi połączeń i transakcji są jednak nadal istotne, dlatego informacje można ekstrapolować, aby mieć tutaj stosunkowo podobny wpływ.
Parametry testu:
Stół:
Operacja:
TRUNCATE TABLE dbo.ManyInserts;
(biorąc pod uwagę charakter tego testu, wykonanie FREEPROCCACHE, FREESYSTEMCACHE i DROPCLEANBUFFERS nie wydawało się dodawać dużej wartości).Wyniki:
Jak widać, nawet jeśli połączenie ADO z bazą danych jest już współużytkowane we wszystkich operacjach, zgrupowanie ich w partie przy użyciu jawnej transakcji (obiekt ADO powinien być w stanie to obsłużyć) jest gwarantowane znacznie (tj. Ponad 2x ulepszenie) skrócić całkowity czas procesu.
źródło
IMHO i pracując z założenia, że nie jest możliwe ponowne kodowanie subwoofera VBA w SQL, czy zastanawiałeś się nad zezwoleniem skryptowi VBA na zakończenie oceny w pliku Excel, a następnie zapisaniu wyników z powrotem na serwerze SQL za pośrednictwem SSIS?
Mógłbyś zacząć i kończyć subwoofm VBA poprzez odwrócenie wskaźnika albo w obiekcie systemu plików, albo na serwerze (jeśli już skonfigurowałeś połączenie do zapisu z powrotem na serwerze), a następnie użyj wyrażenia SSIS, aby sprawdzić ten wskaźnik dla
disable
właściwość danego zadania w ramach rozwiązania SSIS (tak, aby proces importowania czekał, aż podrzędny VBA zakończy się, jeśli obawiasz się, że przekroczy on harmonogram).Dodatkowo, możesz mieć skrypt VBA uruchamiany programowo (trochę nieporadny, ale
workbook_open()
w przeszłości korzystałem z tej właściwości, aby wywoływać zadania tego typu „odpal i zapomnij”).Jeśli czas oceny skryptu VB zaczyna stanowić problem, można sprawdzić, czy programista VB jest skłonny i zdolny do przeniesienia swojego kodu do zadania skryptu VB w ramach rozwiązania SSIS - z mojego doświadczenia wynika, że aplikacja Excel powoduje duże obciążenie, gdy praca z danymi w tym woluminie.
źródło