У меня есть алгоритм, который мне нужно запустить для каждой строки в таблице с 800K строк и 38 столбцов. Алгоритм реализован в VBA и выполняет математические вычисления, используя значения из некоторых столбцов для манипулирования другими столбцами.
В настоящее время я использую Excel (ADO) для запроса SQL и использую VBA с курсорами на стороне клиента, чтобы применить алгоритм по циклам через каждую строку. Работает, но работает 7 часов.
Код VBA является достаточно сложным, поэтому для его перекодировки в T-SQL потребуется много работы.
Я читал об интеграции CLR и UDF как возможных маршрутах. Я также подумал о том, чтобы поместить код VBA в задачу сценария SSIS, чтобы приблизиться к базе данных, но я уверен, что существует экспертная методология для такого типа проблем с производительностью.
В идеале я мог бы запустить алгоритм для максимально возможного числа строк (всех?) В параллельном множестве.
Любая помощь в значительной степени основывалась на том, как добиться максимальной производительности при решении проблем такого типа.
--Редактировать
Спасибо за комментарии, я использую MS SQL 2014 Enterprise, вот еще несколько деталей:
Алгоритм находит характерные шаблоны в данных временных рядов. Функции в алгоритме выполняют полиномиальное сглаживание, управление окнами и находят области интереса на основе критериев ввода, возвращая дюжину значений и некоторые логические результаты.
Мой вопрос больше о методологии, чем о реальном алгоритме: если я хочу добиться параллельных вычислений сразу для нескольких строк, какие у меня варианты?
Я вижу, что рекомендуется перекодировать в T-SQL, но это большая работа, но возможная, однако разработчик алгоритма работает в VBA и часто меняется, поэтому мне нужно синхронизироваться с версией T-SQL и повторно проверять каждый сдача.
Является ли T-SQL единственным способом реализации функций на основе множеств?
источник
N
пакеты и запуститьN
экземпляры вашего алгоритма наN
отдельных процессорах / компьютерах. С другой стороны, каково ваше основное узкое место - перенос данных из SQL Server в Excel или фактические вычисления? Если вы измените функцию VBA для немедленного возврата фиктивного результата, сколько времени займет весь процесс? Если это все еще занимает часы, узкое место в передаче данных. Если это занимает секунды, то вам нужно оптимизировать код VBA, который выполняет вычисления.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 эта функция, которая вызывается для каждой строки, занимает 50 мсек(FileID, RowID)
.Ответы:
Что касается методологии, я полагаю, что вы лаете не на то б-дерево ;-).
Что мы знаем:
Сначала давайте обобщим и рассмотрим, что мы знаем о ситуации:
Существует хранимая процедура, которая вызывается для каждой строки:
Определение (по крайней мере частично):
Что мы можем догадаться:
Затем мы можем рассмотреть все эти точки данных вместе, чтобы увидеть, сможем ли мы синтезировать дополнительные детали, которые помогут нам найти одну или несколько узких мест, и либо указать на решение, либо, по крайней мере, исключить некоторые возможные решения.
Текущее направление мысли в комментариях заключается в том, что основной проблемой является передача данных между SQL Server и Excel. Это действительно так? Если хранимая процедура вызывается для каждой из 800 000 строк и занимает 50 мс на каждый вызов (то есть на каждую строку), это добавляет до 40000 секунд (не мс). И это эквивалентно 666 минутам (ччмм ;-) или чуть более 11 часов. Все же весь процесс, как говорили, занял всего 7 часов. У нас уже 4 часа больше общего времени, и мы даже добавили время, чтобы выполнить вычисления или сохранить результаты обратно на SQL Server. Так что что-то здесь не так.
Глядя на определение хранимой процедуры, есть только входной параметр для
@FileID
; там нет никакого фильтра@RowID
. Поэтому я подозреваю, что происходит один из следующих двух сценариев:@FileID
, которая, по-видимому, охватывает приблизительно 4000 строк. Если указанные 4000 возвращенных строк - это достаточно постоянная сумма, то в 800 000 строк есть только 200 из них. И 200 исполнений, каждый из которых занимает 50 мс, составляют всего 10 секунд из этих 7 часов.@FileID
передача передается в, занимает немного больше времени, чтобы вытянуть новые строки в буферный пул, но тогда следующие 3999 выполнений обычно возвращаются быстрее из-за того, что уже кэшируется, верно?Я думаю, что сосредоточение внимания на этой «фильтрующей» хранимой процедуре или любой передаче данных из SQL Server в Excel - это красная сельдь .
На данный момент, я думаю, наиболее важными показателями слабой производительности являются:
Я подозреваю, что:
UPDATE
заявлений, что составляет 800 000 отдельных транзакций.Моя рекомендация (на основе имеющейся информации):
Ваша самая большая область усовершенствования должна была бы обновить несколько строк за один раз (то есть в одной транзакции). Вы должны обновить свой процесс, чтобы работать с точки зрения каждого
FileID
вместо каждогоRowID
. Так:FileID
в массивFileID
) были вычислены:RowID
Если ваш кластеризованный индекс еще не определен как,
(FileID, RowID)
то вы должны учитывать это (как предложено @MikaelEriksson в комментарии к Вопросу). Это не поможет этим одиночным ОБНОВЛЕНИЯМ, но, по крайней мере, немного улучшит агрегированные операции, например, то, что вы делаете в этой хранимой процедуре «фильтра», поскольку они все основаныFileID
.Вы должны рассмотреть возможность перемещения логики на скомпилированный язык. Я бы предложил создать приложение .NET WinForms или даже консольное приложение. Я предпочитаю консольное приложение, так как его легко планировать с помощью агента SQL или запланированных задач Windows. Не должно иметь значения, делается ли это в VB.NET или C #. VB.NET может быть более естественным для вашего разработчика, но все равно будет некоторая кривая обучения.
На данный момент я не вижу причин переходить на SQLCLR. Если алгоритм часто меняется, это будет раздражать, придется постоянно переустанавливать сборку. Перестройка консольного приложения и размещение .exe-файла в соответствующей общей папке в сети, так что вы просто запускаете одну и ту же программу, и она всегда обновляется, должно быть довольно легко сделать.
Я не думаю, что полное перемещение обработки в T-SQL поможет, если проблема в том, что я подозреваю, и вы просто делаете одно ОБНОВЛЕНИЕ за раз.
Если обработка перемещается в .NET, вы можете использовать табличные параметры (TVP), чтобы передать массив в хранимую процедуру, которая будет вызывать
UPDATE
метод JOINs для табличной переменной TVP и, следовательно, представляет собой одну транзакцию. , TVP должен быть быстрее, чем 4000INSERT
с, сгруппированных в одну транзакцию. Но выигрыш от использования TVP более 4000INSERT
с в 1 транзакции, скорее всего, не будет таким значительным, как улучшение, которое наблюдается при переходе от 800 000 отдельных транзакций к 200 транзакциям по 4000 строк в каждой.Опция TVP изначально не доступна для VBA, но кто-то предложил обходной путь, который может стоить протестировать:
Как повысить производительность базы данных при переходе с VBA на SQL Server 2008 R2?
ЕСЛИ фильтр proc использует только
FileID
вWHERE
предложении, и ЕСЛИ этот proc действительно вызывается для каждой строки, вы можете сэкономить некоторое время обработки, кэшируя результаты первого запуска и используя их для остальных строкFileID
, право?После того, как вы получите обработку сделаны в FILEID , то мы можем начать говорить о параллельной обработке. Но в этом нет необходимости :). Учитывая, что вы имеете дело с 3 довольно крупными неидеальными частями: транзакции Excel, VBA и 800k, любые разговоры об SSIS или параллелограммы, или кто-то знает, что является преждевременной оптимизацией / типом "корзина перед лошадью" , Если мы сможем сократить этот 7-часовой процесс до 10 или менее минут, подумаете ли вы о дополнительных способах его ускорения? Есть ли у вас запланированное время завершения? Имейте в виду, что как только обработка выполняется для каждого идентификатора файла Таким образом, если бы у вас было консольное приложение VB.NET (то есть из командной строки .EXE), ничто не помешало бы запускать несколько из этих FileID одновременно :), независимо от того, был ли это шаг SQL Agent CmdExec или запланированные задачи Windows, и т.п.
И вы всегда можете использовать «поэтапный» подход и вносить несколько улучшений одновременно. Например, начинать с обновлений по одной
FileID
и, следовательно, использовать одну транзакцию для этой группы. Затем посмотрите, сможете ли вы заставить работать TVP. Затем посмотрите, как взять этот код и переместить его в VB.NET (и TVP работают в .NET, поэтому он будет хорошо переноситься).Что мы не знаем, что еще может помочь:
ОБНОВЛЕНИЕ 1:
** Кажется, существует некоторая путаница в отношении того, что такое VBA (Visual Basic для приложений) и что с этим можно сделать, так что это просто для того, чтобы убедиться, что мы все на одной веб-странице:
ОБНОВЛЕНИЕ 2:
Еще один момент для рассмотрения: как обрабатываются соединения? Код VBA открывает и закрывает Соединение для каждой операции, или он открывает соединение в начале процесса и закрывает его в конце процесса (т.е. через 7 часов)? Даже с пулом соединений (который по умолчанию должен быть включен для ADO), все равно должно быть значительное влияние между открытием и закрытием один раз, а не открытием и закрытием либо 800 200, либо 1600 000 раз. Эти значения основаны как минимум на 800 000 ОБНОВЛЕНИЙ плюс 200 или 800 КБ EXEC (в зависимости от того, как часто выполняется хранимая процедура фильтра).
Эта проблема слишком большого количества соединений автоматически смягчается рекомендацией, которую я изложил выше. Создавая транзакцию и делая все ОБНОВЛЕНИЯ в этой транзакции, вы будете держать это соединение открытым и повторно использовать его для каждого
UPDATE
. Независимо от того, остается ли соединение открытым после начального вызова, чтобы получить 4000 строк для указанногоFileID
, или закрыто после этой операции «get» и снова открывается для ОБНОВЛЕНИЙ, это гораздо менее важно, так как мы сейчас говорим о разнице либо Всего 200 или 400 соединений по всему процессу.ОБНОВЛЕНИЕ 3:
Я сделал несколько быстрых испытаний. Пожалуйста, имейте в виду, что это довольно небольшой тест, а не та же самая операция (чистый INSERT против EXEC + UPDATE). Однако различия во времени, относящиеся к тому, как обрабатываются соединения и транзакции, по-прежнему актуальны, поэтому информацию можно экстраполировать, чтобы иметь здесь сравнительно похожее влияние.
Параметры теста:
Стол:
Операция:
TRUNCATE TABLE dbo.ManyInserts;
(учитывая природу этого теста, выполнение FREEPROCCACHE, FREESYSTEMCACHE и DROPCLEANBUFFERS, похоже, не принесло особой пользы.)Результаты:
Как вы можете видеть, даже если соединение ADO с БД уже используется всеми операциями, группировка их в пакеты с использованием явной транзакции (объект ADO должен уметь это обрабатывать) гарантированно значительно (т. Е. Улучшение более чем в 2 раза) сократить общее время процесса.
источник
ИМХО и исходя из предположения, что невозможно перекодировать сабвуфер VBA в SQL, рассматривали ли вы вопрос о том, чтобы позволить сценарию VBA завершить оценку в файле Excel и затем записать результаты обратно на сервер SQL через SSIS?
Можно запустить подпрограмму VBA и завершить ее, щелкнув индикатор либо в объекте файловой системы, либо на сервере (если вы уже настроили соединение для обратной записи на сервер), а затем используйте выражение SSIS, чтобы проверить этот индикатор на наличие
disable
свойство данной задачи в вашем решении SSIS (так что процесс импорта ожидает, пока сабвуфер VBA завершится, если вы беспокоитесь о том, что он превысил свой график).Кроме того, вы можете запустить программный скрипт VBA (немного странно, но
workbook_open()
в прошлом я использовал это свойство для запуска задач « запускай и забывай»).Если время оценки сценария VB начинает становиться проблемой, вы можете увидеть, хочет ли ваш VB-разработчик перенести свой код в задачу сценария VB в рамках решения SSIS - по моему опыту приложение Excel тянет много ресурсов, когда работа с данными на этом томе.
источник