2012-02-09 14 views
2

私はエンタープライズライブラリのDALアプリケーションブロックを介してストアドプロシージャを呼び出し、自分のプロシージャでカスタムテーブルデータ型(@namesNamesTable)として「受信」したDataTableを渡します。この手順は2回目の呼び出しから非常に遅く、パフォーマンスを大幅に向上させるために、実装する方法が異なります。SQL Serverストアドプロシージャのパフォーマンスを向上させる方法?

Names/HistoricalNamesテーブルは膨大で(1億レコード)、これらのテーブルに(データセット/テーブルパラメータを介して)渡されるデータは約400万レコードです)。

基本的には(行う必要がある)ないものを以下の通りです:

  1. NamesまたはHistoricalNamesいずれかの場合
    • チェックのDataTable /テーブルパラメータであるインポート@names(テーブルには、新しいデータセット/テーブルパラメータに含まれる名前のいずれかが含まれています()。インポート全体をスキップして戻る2
    • @namesのすべてのレコードをNamesに挿入し、1を返します。

表には、次のようになります。

create table NameTable 
(
    name nvarchar(20) 
    otherId uniqueidentifier 
) 

これが手順です:

create table Names 
(
    id int IDENTITY(1,1) NOT NULL, 
    name nvarchar(20), 
    otherId uniqueidentifier 
) 

create table HistoricalNames 
(
    id int IDENTITY(1,1) NOT NULL, 
    name nvarchar(20), 
    otherId uniqueidentifier 
) 

表値のパラメータ(@names)は次のようになります。

GO 

SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

ALTER PROCEDURE [dbo].[sp_ImportNames] 
    @names NameTable READONLY 
AS 
BEGIN  
    IF ((SELECT COUNT(cd.name) FROM Names as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0) 
     BEGIN 
      SELECT 2; 
     END 
    ELSE IF ((SELECT COUNT(cd.name) FROM HistoricalNames as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0) 
     BEGIN 
      SELECT 2; 
     END 
    ELSE 
     BEGIN 
      INSERT INTO Names (name, otherId) SELECT * FROM @names; 
      SELECT 1; 
     END 
END 


GO 

パフォーマンスを簡単に調整できますか?どんな助けでも大歓迎です!

+1

これらのテーブルにはどのようなインデックスがありますか? –

+0

スポットがあります。私たちは 'name'カラムにインデックスを持っていましたが、これらは自動的に"スクリプト作成 "には取り込まれませんでした。 – ReFocus

+0

' id'カラムに主キーをクラスター化しましたか? –

答えて

3

ほとんどの場合、テーブル値パラメータが問題になります。

Table Valued Parameter has slow performance because of table scan

これは、テーブルパラメータを使用するための基本的なETLプロセスのために非常に多くのようだが、どのような場合には、テーブル値パラメータがインデックス化されていません。

あなたは、リレーショナルデータベースで見たいものではない、4m行のテーブルスキャンを取得しています。

インデックスを持つステージング領域としてREALテーブルに挿入し、パラメータの代わりにそのテーブルに対して操作を実行すると、大幅なパフォーマンスが向上します。また、他のテーブルにも索引があることを確認してください。

+0

VARIABLE(temp)テーブルに選択するのに役立ちますか?それとも本当のテーブルでなければならないのですか? – ReFocus

+0

@ReFocus http://stackoverflow.com/questions/886050/sql-server-creating-an-index-on-a-table-variable私はちょうど実際のテーブルを使用します。また、従来のETLプロセスと比べてprocに4mの行を送信するという全体的なアーキテクチャを検討する可能性も検討します。それはちょうど非常に珍しいシナリオです。 –

+0

CSVファイルからデータを抽出し、これをProcに送信されるDataTableにロードします。データベースとアプリケーションサーバーは分散されており、CSVをデータベースサーバーに送信することはオプションではありません。 CSVファイル全体を「バッチ」として扱うことができる別の解決方法がありますか? – ReFocus

1

実際の実行計画の表示をオンにします。これは、パフォーマンスが悪い場所を表示します。

+0

'ライブ'ストアドプロシージャの実行プランを表示するにはどうすればよいですか? ... – ReFocus

+0

SSMSからストアドプロシージャを実行するか、アクセスできない場合はdbaを実行して、そこにプランが表示されます。 – Jimbo

2

たぶん、このような何か:

GO 

SET ANSI_NULLS ON 
GO 

SET QUOTED_IDENTIFIER ON 
GO 

ALTER PROCEDURE [dbo].[sp_ImportNames] 
    @names NameTable READONLY 
AS 
BEGIN  
    IF EXISTS(SELECT NULL FROM Names as cd WHERE EXISTS(SELECT NULL FROM @names as c WHERE c.name=cd.name)) 
     BEGIN 
      SELECT 2; 
     END 
    ELSE IF EXISTS(SELECT NULL FROM HistoricalNames as cd WHERE EXISTS(SELECT NULL FROM @names as c WHERE c.name=cd.name)) 
     BEGIN 
      SELECT 2; 
     END 
    ELSE 
     BEGIN 
      INSERT INTO Names (name, otherId) SELECT * FROM @names; 
      SELECT 1; 
     END 
END 
+0

このクエリは、重複が見つかった場合に効果的です。しかし、重複が見つからない場合、Namesテーブルに100000個のレコードが含まれている50.000個のレコードを挿入しようとすると、120秒以上もハングします...これは解決策にはなりませんか? – ReFocus

1

フム。

  • 2つのcehckで「IF NOT EXISTS」という文が1つあります。あなたは毎回完全なカウントを計算しますが、1つのアイテムが存在するかどうかの表示にのみ関心があります。これは高速に実行できます(1つの行が見つかるとクエリを放棄します)。このため、EXISTS句が存在します。
+0

これらを効率的に組み合わせるにはどうすればよいですか?単純なJOINをパラメータなしで使用するだけですか? – ReFocus

1

この量のデータを渡すという問題は、悪い考え方のように聞こえるのを除いて、私が示唆しているのがArionのアプローチです。最初の一致を見つけて成功したという名前の列に索引があると仮定して、どの名前が一致したか、どこで一致したかについての詳細は必要ありません。

私はまたのパフォーマンスに参加を使用して存在してチェックしたい:

if exists(select 1 
from Names exist 
inner join @names newNames on newNames.name = exist.name) 
begin 
    select 2; 
end 

はまた、「一致なし」の場合のために、インサートの列名を明示的に使用が通常は推奨されます注意してください

insert into Names (name, otherId) 
select name, otherId 
from @names 
関連する問題