このように生成される挿入クエリがあります
INSERT INTO InvoiceDetail (LegacyId,InvoiceId,DetailTypeId,Fee,FeeTax,Investigatorid,SalespersonId,CreateDate,CreatedById,IsChargeBack,Expense,RepoAgentId,PayeeName,ExpensePaymentId,AdjustDetailId)
VALUES(1,1,2,1500.0000,0.0000,163,1002,'11/30/2001 12:00:00 AM',1116,0,550.0000,850,NULL,@ExpensePay1,NULL);
DECLARE @InvDetail1 INT; SET @InvDetail1 = (SELECT @@IDENTITY);
このクエリは、110K行だけに対して生成されます。
これらすべてのクエリの実行には30分かかります
クエリプランを確認したところ、最大の%ノードは
57%のクエリコストでクラスター化されたインデックスの挿入。これには、投稿したくない長いxmlがあります。
クエリコストが38%のテーブルスプール
<RelOp AvgRowSize="35" EstimateCPU="5.01038E-05" EstimateIO="0" EstimateRebinds="0" EstimateRewinds="0" EstimateRows="1" LogicalOp="Eager Spool" NodeId="80" Parallel="false" PhysicalOp="Table Spool" EstimatedTotalSubtreeCost="0.0466109">
<OutputList>
<ColumnReference Database="[SkipPro]" Schema="[dbo]" Table="[InvoiceDetail]" Column="InvoiceId" />
<ColumnReference Database="[SkipPro]" Schema="[dbo]" Table="[InvoiceDetail]" Column="InvestigatorId" />
<ColumnReference Column="Expr1054" />
<ColumnReference Column="Expr1055" />
</OutputList>
<Spool PrimaryNodeId="3" />
</RelOp>
だから私の質問は、このことの速度を向上させるために私ができることは何ですか?クエリの前にALTER TABLE TABLENAME NOCHECK CONSTRAINTS ALLを実行し、クエリの後でALTER TABLE TABLENAME NOCHECK CONSTRAINTS ALLを実行しました。
そして、それは時間のほとんど何も削り落としませんでした。
SqlCommandオブジェクトを使用してクエリを送信する.NETアプリケーションでこれらのクエリを実行していることを知っています。
次に、sqlコマンドをファイルに出力し、sqlcmdを使用して実行しようとしましたが、動作を更新することができなかったため、あきらめました。
アイデアやヒント、助けはありますか?
更新:
わかりましたので、皆はとても役に立ちました。このような状況で、私は複数の答えを信用できればと思います。
これを修正する解決策は2つありました。
最初:
1)すべての外部キーを無効化/再有効化しました(ドロップするよりもはるかに簡単です)
ALTER TABLE TableName NOCHECK CONSTRAINT ALL
ALTER TABLE TableName CHECK CONSTRAINT ALL
2)インデックスを無効化/再有効化しました(ここでも、削除するよりもはるかに簡単です)
ALTER INDEX [IX_InvoiceDetail_1] ON [dbo].[InvoiceDetail] DISABLE
ALTER INDEX [IX_InvoiceDetail_1] ON [dbo].[InvoiceDetail] REBUILD PARTITION = ALL WITH ( PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, ONLINE = OFF, SORT_IN_TEMPDB = OFF )
二番目:
すべての挿入ステートメントを1つのトランザクションにラップしました。私は最初、.NETでそれを行う方法を知りませんでした。
私は得たすべての入力に本当に感謝しています。
このようなDBからDBへの変換を行う場合は、必ずBULK INSERTから始めます。はるかに柔軟で高速です。
挿入のように聞こえるため、SQL Serverはインデックスを再計算します。 1つ可能解決策は、インデックスを削除し、挿入を実行して、インデックスを再度追加することです。試行したソリューションでは、制約を無視するように指示した場合でも、インデックスを更新し続ける必要があります。
これらのクエリを.Netクライアントから一度に1つずつ実行していますか(つまり、110,000の個別のクエリ要求をSQL Serverに送信していますか)?
その場合は、これらのINSERTをSQL Server自体ではなく、バッチ処理せずにSQL Serverに送信することによるネットワークの待ち時間やその他のオーバーヘッドが原因である可能性があります。
BULK INSERTを確認してください。
ほとんどの場合、これはコミットフラッシュ待機です。 INSERTのセットを明示的に管理されたトランザクションにラップしない場合、各INSERTは独自の自動コミットされたトランザクションです。つまり、各INSERTは自動的にコミットを発行し、コミットはログが永続化される(ディスクに書き込まれる)まで待機する必要があります。各挿入後のログのフラッシュは非常に遅いです。
たとえば、1行のコミットスタイルで100k行を挿入しようとすると、次のようになります。
set nocount on;
declare @start datetime = getutcdate();
declare @i int = 0;
while @i < 100000
begin
INSERT INTO InvoiceDetail (
LegacyId,InvoiceId,DetailTypeId,Fee,
FeeTax,Investigatorid,SalespersonId,
CreateDate,CreatedById,IsChargeBack,
Expense,RepoAgentId,PayeeName,ExpensePaymentId,
AdjustDetailId)
VALUES(1,1,2,1500.0000,0.0000,163,1002,
'11/30/2001 12:00:00 AM',
1116,0,550.0000,850,NULL,1,NULL);
set @i = @i+1;
end
select datediff(ms, @start, getutcdate());
これは私のサーバーで約12秒で実行されます。しかし、トランザクション管理を追加し、1000行ごとにコミットすると、100k行の挿入は約4秒しか持続しません。
set nocount on;
declare @start datetime = getutcdate();
declare @i int = 0;
begin transaction
while @i < 100000
begin
INSERT INTO InvoiceDetail (
LegacyId,InvoiceId,DetailTypeId,
Fee,FeeTax,Investigatorid,
SalespersonId,CreateDate,CreatedById,
IsChargeBack,Expense,RepoAgentId,
PayeeName,ExpensePaymentId,AdjustDetailId)
VALUES(1,1,2,1500.0000,0.0000,163,1002,
'11/30/2001 12:00:00 AM',
1116,0,550.0000,850,NULL,1,NULL);
set @i = @i+1;
if (@i%1000 = 0)
begin
commit
begin transaction
end
end
commit;
select datediff(ms, @start, getutcdate());
また、バッチコミットなしでも12秒で100万行を挿入できることを考えると、30分必要ですが、調査する価値はあります。1)IOサブシステムの速度(たとえば、Avg. Sec per Transaction
ドライブに表示されます)および2)1つの呼び出しから@@ identityを取得してから次の挿入を呼び出すまでの間にクライアントコードは何をしていますか。時間の大部分がスタックのクライアント側にある可能性があります。簡単な解決策の1つは、複数の挿入を並行して起動すること(BeginExecuteNonQuery)で、SQL Serverの挿入を常にフィードすることです。
この質問には「bulkinsert」というタグを付けています。それでは、なぜ BULK INSERT コマンドを使用しないのですか?
進行状況の更新が必要な場合は、一括挿入を小さな部分に分割し、各部分が完了した後に進行状況を更新できます。
できることがいくつかあります。
1) Disable any triggers on this table
2) Drop all indexes
3) Drop all foreign keys
4) Disable any check constraints
個々のINSERTの実行は、常に最も遅いオプションになります。また、@@ IDENTITYとの取り決めは何ですか?それらの間を追跡する必要があるようには見えません。
ファイルまたはSSISからのBULK INSERTを使用しない場合は、 ADO.NETのSqlBulkCopy機能 を使用します。プログラム。
11万行は、この回答を調べて書くよりもインポートにかかる時間が短いはずです。
挿入パフォーマンスを向上させるためのいくつかの提案:
うーん、実行して、パフォーマンスカウンターを確認してください。何が見えますか?どのようなディスクレイアウトがありますか? 30分で数百万行を挿入できます。正確には1億行近くです(リアルタイムの財務情報、他の3つのテーブルへのリンク)。私はあなたのIOレイアウトが悪い(すなわち、悪いディスク構造、悪いファイル配布)であることをかなり賭けます