MS SQL Serverのデータの一括挿入の最適化の詳細

あなたはどのように行うだろうか長い間、MSSQLの必要性を次のように今、次のようにMSSQLデータベースのテーブル構造へのデータの10ワット挿入する需要があるされ、あなたがテーブルにデータを挿入し10Wストリップを感じますか?

またはあなたのバルクデータは、それを挿入する方法ですか?私は今日になるこの問題を議論します。

 

見た目に圧力を測定するMVC HTTPデータ・インタフェース

まず、ちょうど開口部のニーズに関連してセントずに、データベースに挿入されたパフォーマンスの条件を理解するために、次への参照であるためにここにさせて頂いております。

MVCインタフェースコードは次のよう:

パブリックブール追加(CustomerFeedbackEntity M)
        { 
            (VAR CONN =接続)を使用して
            、{ 
                [CustomerFeedback】文字列のSQL = @ "INSERT INTO [DBO]を
                                           ([BUSTYPE] 
                                           、[CustomerPhone] 
                                           、[BackType] 
                                           、[コンテンツ] 
                                     VALUES 
                                           (@ BUSTYPE 
                                           、@ CustomerPhone 
                                           、@ BackType 
                                           、@コンテンツ 
                                           )」; 
                リターンconn.Execute(SQL、M)> 0; 
            } 
        }
 

単一のインターフェイスにこの圧力測定データは、図のデータベース内のデータを集約MVCします。

このような使用の例:5000件の要求の実行スレッド500ポイントポスト要求インタフェース。

このチャートは、最も遅いが、それをわずか4ミリ秒を要求することを教えてくれる。だから我々はアルゴリズムでなければなりません。

需要の開口部は、我々は最小応答時間が計算されます。

10ワット、データのデータベース必要* 4 = 100 000ミリ秒、約6.67分に挿入しました。その後、我々は挿入プログラムを作るためにこの目標を走りました。

最も一般的な方法を挿入

次のように最初に我々は、それがコードセクションを書く必要がエンジニアを取得します:

//データの個数実行
        INT * 10000 CNT = 10; 
        //データが挿入される
        CustomerFeedbackEntity M =新CustomerFeedbackEntity(){ BUSTYPE = 1、CustomerPhone = "1888888888"、BackType = 1、コンテンツ=「123123dagvhkfhsdjk 確か損なわ活気保護分類カルマハスデ}高いレートグランドオープン」を台無し; 
        //最初の
        公共ボイドFristWay()
        { 
            使用(VAR =新しい新しいコネチカットSqlConnectionオブジェクト(ConnStr))
            { 
                conn.Open(); 
                ストップウォッチザ・ストップウォッチ新しい新しいSW =( ); 
                sw.Start(); 
                のStringBuilder StringBuilderの新しい新しいSB =(); 
                { 
                    sb.Clear()。 
                Console.WriteLineを( "FROM:" + DateTime.Now.ToString( "YYYY- MM-DD HH:MM:SSのFFF")+」は、 実行サイクルを開始します: "+ CNT +"バーSQL文...「);
                (;; I <= CNT I ++のint iは= 0)のため
                    sb.Append(@ "INSERT INTO [DBO] [CustomerFeedback]。
                                           ([BUSTYPE] 
                                           、[CustomerPhone] 
                                           、[BackType] 
                                           、[コンテンツ] 
                                     VALUES("); 
                    sb.Append(m.BusType); 
                    sb.Append( 」、 ' "); 
                    sb.Append(m.CustomerPhone); 
                    sb.Append("' "); 
                    sb.Append(m.BackType); 
                    sb.Append("、 ""); 
                    SB。追加(m.Content)。 
                    sb.append( "「)"); 
                    を使用して)(SqlCommandオブジェクトSqlCommandオブジェクトは=新しい新しい(sb.ToString()、コネチカットCMD)
                    { 
                        ; cmd.CommandTimeout = 0 
                        cmd.ExecuteNonQueryを(); 
                    } 
                } 
                Console.WriteLineを(DateTime.Now.ToString( "MM-DD-YYYY HH:MM:SS FFF")+ "実行:" + CNT + "処理SQL文完成物品:!" + + sw.ElapsedMilliseconds "MS。"); 
            } 
        }
 

次のように実行結果は以下のとおりです。

データ、693906ミリ秒、11分の10ワットは、何の感情は大丈夫存在しない、またはそれはまた、許容可能です。書き込みを続け、私は血を吐いたように話せない親は、あなたは、MSSQLデータベースおよび.NETフィット以降を参照してください?

下のコメント:

sqlcommonオブジェクトを作成し、解放するために1、ノンストップ、パフォーマンスが無駄になります。

2、データベースへのノンストップ接続は、パフォーマンスの大幅な損失が発生します。

同様に、この二時の結果は、データ挿入の最も一般的な方法であったとしても、この方法が望ましくないことを教えてくれる。

我々は2ポイント、1を超えるために最適化されませんので、唯一のデータベースへの接続を確立するために、sqlcommonオブジェクトを作成します。変換コードを最適化以下の通りであります:

公共ボイドSecondWay()
        { 
            (VAR CONN =新しいSqlConnectionオブジェクト(ConnStr))を使用して
            { 
                conn.Open()と、
                ストップウォッチSW =新しいストップウォッチ(); 
                sw.Start(); 
                StringBuilderのSB =新しいStringBuilderの(); 
                Console.WriteLineを( "从:" + DateTime.Now.ToString( "YYYY-MM-DD HH:MM:SSのFFF")+ "开始循环拼接:" + CNT + "条SQL语句..."); 
                以下のために(INT I = 0; I <= CNT; I ++)
                { 
                    。sb.Append(@ "INSERT INTO [DBO] [CustomerFeedback] 
                                           ([BUSTYPE] 
                                           、[CustomerPhone]
                                           [BackType] 
                                           、[コンテンツ] 
                                     VALUES( "); 
                    sb.Append(m.BusType); 
                    sb.Append("、 ' "); 
                    sb.Append(m.CustomerPhone); 
                    sb.Append("'、」 ); 
                    sb.Append(m.BackType); 
                    sb.Append( " ' "); 
                    sb.Append(m.Content); 
                    sb.Append("')"); 
                } 
                VAR結果= sw.ElapsedMilliseconds。時間のかかる: "+ sw1.ElapsedMilliseconds +" ミリ秒。
                「); }
            } 
        }
 

次のように実行結果は以下のとおりです。

うん、奇妙なああ、なぜスキームを維持することが作るん大きな違いではないでしょうか?

まず、私たちは、このようなAを探すには、長いSQL文のスプライシングは、データベースが実行する方法である方法です。

データベース接続を確認してください。1.

(名前= 'DBNAME'のsysdatabasesから選択DBID)でDBIDのsysprocessesから選択* 
-或者
SELECT * FROM 
。。[マスター] [DBO] [SYSPROCESSES] WHERE [DBID] IN(SELECT 
   [DBID] 
FROM 
   [マスター] [DBO]。[sysdatabasesの】
WHERE 
   NAME = 'DBNAME' 
 

2、データベースをチェックするには、SQL文が実行されています

SELECT [SPID] = session_idの、
            ECID、
            [データベース] = DB_NAME(sp.dbid)、
            [ユーザー] = nt_username、
            [状態] = er.status、
            [待ち] = wait_type、
            [個別クエリ] = SUBSTRING(qt.text 、
                                           er.statement_start_offset / 2、
                                           (CASE WHEN er.statement_end_offset = -1 
                                                  THEN LEN(CONVERT(NVARCHAR(MAX)、qt.text))
                                                       * 2 
                                                  ELSE er.statement_end_offset
                                             END - er.statement_start_offset)
                                           / 2)、
            [親クエリ] = qt.text、
            プログラム= program_nameの、
            ホスト名、
            nt_domain、
            START_TIME 
    sys.dm_exec_requestsのERから
            INNER JOIN sys.sysprocesses SP上er.session_id = sp.spid 
            CROSSは、SYSを適用しますQT AS .dm_exec_sql_text(er.sql_handle)
    システムのSPIDを無視- SESSION_ID> 50。
            AND NOT IN(@@ SPID)はSESSION_ID -この現在の文を無視します。
ORDER BY 1、
            2
 

コメント:解決策の実行中に、実際には、最適化され得るように見えるかもしれないがそれは何を言うまでもない、ほぼ同じです。

ユーザー定義テーブル型のMSSQLデータベースに資する最適化を行います

それでも最初のコードの上に、あなたはおそらく興味のテーブルタイプをユーザーが定義することができます。

TYPE CustomerFeedbackTemp AS TABLE(CREATE 
BUSTYPE INT NOT NULL、
CustomerPhone VARCHAR(40)NOT NULL、
BackType INT NOT NULL、
コンテンツデータ型はnvarchar(1000)NOT NULLを
 
公共ボイドThirdWay()
        { 
            ストップウォッチSW =新しいストップウォッチ()。
            ストップウォッチSW1 =新しいストップウォッチ(); 
            データテーブルDT = GetTable()。
            使用した(するvar CONN =新しいSqlConnectionオブジェクト(ConnStr))
            { 
                文字列のsql = @ "INSERT INTO [DBO]。[CustomerFeedback] 
                                           ([BUSTYPE] 
                                           、[CustomerPhone] 
                                           、[BackType] 
                                           、[コンテンツ] 
                                          )を選択しBUSTYPE、CustomerPhone、BackType、 @TempTbから[コンテンツ] ";
                (SqlCommandオブジェクトCMD =新しいSqlCommandオブジェクト(SQL、CONN))を使用して、
                { 
                    cmd.CommandTimeout = 0。
                    SqlParameter catParam = cmd.Parameters.AddWithValue( "TempTb @"、DT); 
                    catParam.SqlDbType = SqlDbType.Structured。
                    catParam.TypeName = "dbo.CustomerFeedbackTemp"。
                    conn.Open(); 
                    Console.WriteLineを( "从:" + DateTime.Now.ToString( "YYYY-MM-DD HH:MM:SSのFFF")+ "开始循环插入内存表中:" + CNT + "条数据...") ; 
                    sw.Start(); 
                    以下のために(INT I 0 =; I <CNT; I ++)
                    { 
                        のDataRow DR = dt.NewRow()。
                        DR [0] = m.BusType; 
                        DR [1] = m.CustomerPhone; 
                        DR [2] = m.BackType; 
                        DR [3] = m.Content; 
                        dt.Rows.Add(DR); 
                    } 
                    Console.WriteLineを( DateTime.Now.ToString( "YYYY-MM-DD HH:MM:SSのFFF")+ " ループがメモリテーブルに挿入されている:" + CNT + "処理されるデータの完了:!" + sw.ElapsedMilliseconds +「ミリ。 「); 
                    sw1.Start(); 
                    IF(= NULL && dt.Rows.Count DT = 0)!
                    { 
                        cmd.ExecuteNonQuery(); 
                        sw.Stop(); 
                    }
                    Console.WriteLineを(DateTime.Now.ToString( "YYYY-MMは、 " +)SSのFFF:MM:HHを-dd " 実行、" + CNT + + ":!加工データベースのデータへのデータのデータテーブル" SW1 .ElapsedMilliseconds + "秒。"); 
                } 
            } 
        }
 

結果:

うわー迅速な問題は、それを実行されていないと信じてすることはできませんどのように4ミリ秒ごとに、より少ない2秒未満、2秒以上、Qを逮捕しました。

再びそれを行います

再びそれを行います

はい、あなたは、右の2秒未満のデータの10ワットをお読みください。なぜ知っている待っていませんか?私たちはそれを使用するもの、ユーザー定義テーブル型を知るために待つことができませんか?

ユーザー定義テーブル型

まず第一に、それはすべてのタイプがあり、このテーブルは、羊毛、それのタイプがあるint型、varchar型、ビットのように、種類を理解することは非常に簡単にすべきですか?

実際に、彼は、ユーザーが自分のテーブル構造を定義し、タイプとして彼を考えることができます。

詳細なドキュメントのカスタムタイプの作成:https://msdn.microsoft.com/zh-cn/library/ms175007.aspx 

第二に、定義されたタイプは、いくつかの制限があり、セキュリティ:https://msdn.microsoft.com/zh-cn/library/bb522526.aspx

このタイプは、どのようにしている、彼が使用するテーブル値パラメータとして使用されます。

テーブルのパラメータ値は、複数の行または(例えばストアド・プロシージャまたは関数など)のTransact-SQLステートメントルーチンにデータを送信することができ、一時テーブルまたはパラメータの数を作成するために必要ではないかもしれません。

テーブル値類似のOLE DBとODBCパラメーター配列を有するパラメータが、近い方のTransact-SQLと高い柔軟性と統合しています。別の利点は、データセットに基づいて、操作に関与するパラメータ値表です。

Transact-SQLのテーブルパラメータは、入力データのコピーを作成しないように、参照することにより、ルーチンに渡されます。あなたは、作成および使用テーブル値パラメーターというのTransact-SQLルーチンを実行し、とTransact-SQLコード、管理クライアント、およびネイティブクライアントからそれらを呼び出すために任意の管理対象の言語を使用することができます。

利点

他のパラメータのような、テーブル値パラメータの範囲は、プロシージャ、関数または動的のTransact-SQL文を記憶されているように。同様に、他のローカル変数、テーブル型の変数は、DECLAREステートメントを使用して作成されている同じスコープを持っています。あなたは、動的のTransact-SQLステートメント内の変数テーブルの値を宣言することができ、これらの変数は、テーブルやストアドプロシージャパラメータとして関数に渡すことができます。

転送リストのパラメータテーブルは、いくつかのケースでは、より大きな柔軟性を持っていることの価値、一時テーブル、または他の同等の方法は、より優れたパフォーマンスを提供します。テーブル値パラメータは、次のような利点があります。

  • あなたは、クライアントからのデータを埋める初めてのロックを取得できません。

  • これは、単純なプログラミングモデルを提供します。

  • これは、単一のルーチンに含める複雑なビジネスロジックを可能にします。

  • サーバーへのラウンドトリップを減らします。

  • テーブル構造は、異なる塩基を有していてもよいです。

  • それは、強く型付けされています。

  • これは、クライアントがソート順とユニークなキーを指定することができます。

  • ストアド・プロシージャとして一時テーブルとして使用する場合は、キャッシュされています。SQL Serverの2012以降では、パラメータ化クエリのため、テーブル値パラメータもキャッシュすることができます。

制限事項

TABLEパラメータは、次の制限があります。

  • SQL Serverは、テーブル値パラメーターの列に関する統計を維持していません。

  • テーブル値パラメーターは、R​​EADONLYのTransact-SQLルーチンへの入力パラメータとして渡す必要があります。パラメータの値の表は、ルーチン本体のUPDATE、DELETE、INSERT、またはそのようなDML操作として行うことができません。

  • 表は、パラメータSELECT INTOまたはINSERT EXECステートメントの目標値として使用されていません。TABLE SELECT INTOパラメータは、FROM句ことができ、あなたは、INSERT EXEC文字列またはストレージであってもよいです。

最適化に共通のBULK INSERTのデータセット

公共ボイドFourWay()
        { 

            ストップウォッチSW =新しいストップウォッチ()。
            ストップウォッチSW1 =新しいストップウォッチ(); 
            データテーブルDT = GetTable()。
            使用(SqlConnectionのCONN =新しいSqlConnectionオブジェクト(ConnStr))
            { 
                SqlBulkCopyバルク・コピー=新しいSqlBulkCopy(CONN)。
                bulkCopy.BulkCopyTimeout = 0; 
                bulkCopy.DestinationTableName = "CustomerFeedback"。
                bulkCopy.BatchSize = dt.Rows.Count。
                conn.Open(); 
                Console.WriteLineを( "从:" + DateTime.Now.ToString( "YYYY-MM-DD HH:MM:SSのFFF")+ "开始循环插入内存表中:" + CNT + "条数据...") ; 
                sw.Start();
                以下のために(INT I 0 =; I <CNT; I ++)
                { 
                    のDataRow DR = dt.NewRow()。
                    DR [0] = m.BusType。
                    DR [1] = m.CustomerPhone。
                    DR [2] = m.BackType。
                    DR [3] = m.Content。
                    dt.Rows.Add(DR)。
                } 
                Console.WriteLineを(DateTime.Now.ToString( "YYYY-MM-DD HH:MM:SSのFFF")+ "时、循环插入内存表:" + CNT + "条数据完成耗时:!" + SW。 ElapsedMilliseconds + "毫秒。"); 
                sw1.Start(); 
                (!DT = NULL && dt.Rows.Count = 0)であれば
                { 
                    bulkCopy.WriteToServer(DT)。
                } 
                Console.WriteLineを(DateTime.Now.ToString( "MM-DD-YYYY HH:MM:SS FFF")+ "実行:" + CNT + "処理データベースデータへのデータのデータテーブル:!" + sw1.ElapsedMilliseconds + "秒。"); 
            }
 

結果:

これは単純に10ワット、ガード、ガードああ挿入1秒のリズムストリップデータ内に完了すること読んだ後、完全に1秒、1秒以内に完了。

詳細一括挿入:https://msdn.microsoft.com/zh-cn/library/ms188365.aspx

プロフェッショナルレビュー:

使用される方法は、可変データセットに基づいて、他の方法を用いて、パラメータテーブルの値と同様であるが、パラメータ値テーブルを頻繁に使用する高速大規模なデータセットに比べ。バルク操作起動コストは、パラメータテーブルの値と比較し、パラメータ値テーブルより大きい未満1000に挿入され、行の数は、良好な性能で行われます。

再利用可能なテーブル値パラメータは、一時テーブルキャッシュの恩恵を受けることができます。このテーブルキャッシュ機能優れた拡張性を提供するために、比較可能な同等のBULK INSERT操作。小さな行挿入を使用する場合、小さなパフォーマンスの向上パラメータリストまたはバッチ文(BULK INSERT操作または代わりのパラメータ値テーブル)を使用することによって得ることができます。しかし、これらの方法はあまり便利でプログラミングされ、そしてラインの増加とともに、パフォーマンスが急速に低下します。

このようにかなりより良いの配列としての性能パラメータの実装と実現におけるパラメータテーブルの値。

おすすめ

転載: www.cnblogs.com/ShaYeBlog/p/12204273.html