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は、セットベースの関数を実装する唯一の方法ですか?
方法論に関しては、あなたは間違ったbツリーを始めていると思います;-)。
知っていること:
最初に、状況について私たちが知っていることを統合して確認しましょう。
すべての行に対して呼び出されるストアドプロシージャがあります。
定義(少なくとも一部)は次のとおりです。
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
推測できること:
次に、これらすべてのデータポイントを一緒に見て、1つ以上のボトルネックを見つけるのに役立つ追加の詳細を合成できるかどうかを確認し、解決策を指すか、少なくともいくつかの可能な解決策を除外できます。
コメントでの現在の考え方は、SQL ServerとExcel間のデータ転送が主要な問題であることです。それは本当ですか?ストアドプロシージャが800,000行ごとに呼び出され、呼び出しごとに(つまり、各行ごとに)50ミリ秒かかる場合、合計で40,000秒(ミリ秒ではない)になります。これは666分(hhmm ;-)、つまり11時間強に相当します。しかし、プロセス全体の実行には7時間しかかからないと言われています。合計時間はすでに4時間です。計算を実行したり、結果をSQL Serverに保存したりするために時間を追加することもできます。だから何かがここでは正しくありません。
ストアドプロシージャの定義を見ると、@FileID
の入力パラメーターしかありません。 @RowID
にはフィルタはありません。したがって、次の2つのシナリオのいずれかが発生していると思います。
@FileID
ごとに呼び出されます。これは約4000行にわたるようです。返された前述の4000行がかなり一貫している場合、800,000行にグループ化されているのは200行のみです。そして、それぞれ50ミリ秒かかる200回の実行は、その7時間のうちわずか10秒です。@FileID
が初めて渡されたときに、新しい行がバッファプールにプルされるまでに少し時間がかかりますが、通常、次の3999の実行は返されます。すでにキャッシュされているため、より高速ですよね?この「フィルター」ストアドプロシージャ、またはSQL ServerからExcelへのデータ転送に焦点を当てることは red herring だと思います。
現時点では、パフォーマンスの低下の最も関連する指標は次のとおりです。
私はそれを疑っています:
UPDATE
ステートメントを発行することです。これは、800,000の個別のトランザクションです。私の推薦(現在入手可能な情報に基づく):
改善の最大の領域は、一度に(つまり、1つのトランザクションで)複数の行を更新することです。各FileID
ではなく、各RowID
で機能するようにプロセスを更新する必要があります。そう:
FileID
の4000行すべてを配列に読み込みますFileID
)が計算されると、次のようになります:RowID
ごとに呼び出すクラスター化インデックスが(FileID, RowID)
としてまだ定義されていない場合は、それを考慮する必要があります(質問のコメントで@MikaelErikssonが提案したとおり)。これらのシングルトンUPDATEは役立ちませんが、すべてのFileID
に基づいているため、「フィルター」ストアドプロシージャで実行している操作など、集計操作が少なくともわずかに改善されます。
ロジックをコンパイル済み言語に移動することを検討する必要があります。 .NET WinFormsアプリまたはコンソールアプリを作成することをお勧めします。 SQLエージェントまたはWindowsのスケジュールされたタスクを介して簡単にスケジュールできるので、コンソールアプリを好みます。 VB.NETとC#のどちらで行われるかは問題ではありません。 VB.NETは開発者にとってより自然な適合かもしれませんが、学習曲線はまだあります。
現時点では、SQLCLRに移行する理由はありません。アルゴリズムが頻繁に変更されると、アセンブリを常に再配置する必要があり、煩わしくなります。コンソールアプリを再構築し、.exeをネットワーク上の適切な共有フォルダーに配置することで、同じプログラムを実行するだけで常に最新の状態になり、非常に簡単に実行できるようになります。
問題が疑わしいものであり、一度に1つのUPDATEを実行しているだけの場合、処理をT-SQLに完全に移動しても効果があるとは思いません。
処理が.NETに移された場合、テーブル値パラメーター(TVP)を利用して、配列をストアドプロシージャに渡し、UPDATE
を呼び出してTVPテーブル変数に結合することができます。したがって、単一のトランザクションです。 TVPは、単一のトランザクションにグループ化された4000 INSERT
sを実行するよりも高速である必要があります。しかし、1つのトランザクションで4000 INSERT
sを超えるTVPを使用することによる利益は、800,000の個別のトランザクションからそれぞれ4000行の200のトランザクションのみに移行した場合に見られる改善ほど大きくはないでしょう。
TVPオプションはVBA側ではネイティブに利用できませんが、誰かがテストに値するかもしれない回避策を考え出しました:
VBAからSQL Server 2008 R2に移行するときにデータベースのパフォーマンスを向上させるにはどうすればよいですか?
フィルタープロシージャがFileID
句でWHERE
のみを使用しており、そのプロシージャが実際にすべての行ごとに呼び出されている場合、最初の実行の結果をキャッシュすることで、処理時間を節約できます。そして、それらの行ごとの残りの行にそれらを使用しますFileID
、そうですか?
処理が完了したら、-FileID、thenで並列処理について説明します。しかし、それはその時点では必要ないかもしれません:)。 Excel、VBA、800kトランザクションの3つのかなり非理想的な部分を扱っているとすれば、SSISや平行四辺形の話、または誰が何を知っているかは、時期尚早な最適化/馬の前のカートタイプのものです。 。この7時間のプロセスを10分以下に短縮できるとしたら、それをさらに速くするための追加の方法を考えていますか?あなたが考えている目標完了時間はありますか?処理がファイルIDごとベースで行われると、VB.NETコンソールアプリケーション(つまり、コマンドライン.EXE)がある場合、いくつかの実行を妨げるものは何もないことに注意してください。これらのFileIDは一度に:)、SQLエージェントのCmdExecステップまたはWindowsのスケジュールされたタスクなどを介して.
また、いつでも「段階的」アプローチを取り、一度にいくつかの改善を行うことができます。たとえば、FileID
ごとに更新を行うことから始めて、そのグループに対して1つのトランザクションを使用します。次に、TVPを機能させることができるかどうかを確認します。次に、そのコードを取得してVB.NETに移動する方法を確認します(TVPは.NETで機能するため、適切に移植できます)。
まだ役に立たないとわかっていること:
更新1:
** どのVBA(Visual Basic for Applications)とそれを使って何ができるかについて混乱があるようですが、これは私たち全員が同じWebページにいることを確認するためのものです。
更新2:
考慮すべきもう1つのポイント:接続はどのように処理されますか? VBAコードは、各操作ごとに接続を開いたり閉じたりしていますか?それとも、プロセスの開始時に接続を開き、プロセスの最後(つまり7時間後)に接続を閉じますか?接続プール(デフォルトではADOで有効にする必要があります)があっても、800,200回または1,600,000回の開閉ではなく、1回の開閉の間にかなりの影響があります。これらの値は、少なくとも800,000の更新と200または800kのEXECに基づいています(フィルターストアドプロシージャが実際に実行される頻度によって異なります)。
接続が多すぎるというこの問題は、上で概説した推奨事項によって自動的に緩和されます。トランザクションを作成し、そのトランザクション内ですべてのUPDATEを実行することにより、その接続を開いたままにして、UPDATE
ごとにそれを再利用します。指定されたFileID
ごとに4000行を取得するための最初の呼び出しから接続を開いたままにするか、その「取得」操作の後で閉じてUPDATEのために再度開くかは、現在のところ影響がはるかに少ないプロセス全体での合計接続数が200または400の違いについて話している。
更新3:
簡単なテストをいくつか行いました。これはかなり小規模のテストであり、まったく同じ操作ではないことに注意してください(純粋なINSERT対EXEC + UPDATE)。ただし、接続とトランザクションの処理方法に関連するタイミングの違いは依然として関連しているため、ここでは比較的類似した影響があると推定できます。
テストパラメータ:
テーブル:
CREATE TABLE dbo.ManyInserts
(
RowID INT NOT NULL IDENTITY(1, 1) PRIMARY KEY,
InsertTime DATETIME NOT NULL DEFAULT (GETDATE()),
SomeValue BIGINT NULL
);
操作:
INSERT INTO dbo.ManyInserts (SomeValue) VALUES ({LoopIndex * 12});
TRUNCATE TABLE dbo.ManyInserts;
(このテストの性質上、FREEPROCCACHE、FREESYSTEMCACHE、およびDROPCLEANBUFFERSを実行しても、それほどの付加価値はないようです。)結果:
Test Milliseconds
------- ------------
10k INSERTs across 10k Connections 3968 - 4163
10k INSERTs across 1 Connection 3466 - 3654
10k INSERTs across 1 Transaction 1074 - 1086
10k INSERTs across 10 Transactions 1095 - 1169
ご覧のとおり、DBへのADO接続がすでにすべての操作で共有されている場合でも、明示的なトランザクションを使用してそれらをバッチにグループ化します(ADOオブジェクトは、これにより)全体の処理時間を大幅に(つまり2倍以上に)短縮できることが保証されています。
私見で、VBAサブをSQLに再コード化することは不可能であるという前提から作業していますが、VBAスクリプトがExcelファイルで評価を完了し、結果をSSIS経由でSQLサーバーに書き込むことを許可することを検討しましたか?
VBAサブの開始と終了をファイルシステムオブジェクトまたはサーバーのインジケーターの反転で終了し(サーバーに書き戻すように接続を既に構成している場合)、SSIS式を使用してこのインジケーターを確認します。 SSISソリューション内の特定のタスクのdisable
プロパティ(そのため、スケジュールの超過を心配している場合、インポートプロセスはVBAサブが完了するまで待機します)。
さらに、VBAスクリプトをプログラムで開始させることもできます(少し不安定ですが、workbook_open()
プロパティを使用して、過去にこの種の「起動して忘れる」タスクをトリガーしました)。
VBスクリプトの評価時間が問題になり始めたら、VB開発者が彼のコードをVBスクリプトに移植できるかどうかを確認できますSSISソリューション内のタスク-私の経験では、Excelアプリケーションは、このボリュームでデータを操作するときに多くのオーバーヘッドを引き出します。