web-dev-qa-db-ja.com

SQL Serverデータベースにデータのコレクションを挿入するベストプラクティスは何ですか

私はC#、Entity Frameworkを使用しています。開発用のSQL Server。 2つのパラメーター(int ID, List<String> _listobj)を持つメソッドがあります。 IDごとに、objのリストがあります。これらのIDとリストはすべて、データベースの異なるテーブルに挿入されます。だから問題は:これのためのベストプラクティスは何ですか?私にはいくつかのオプションがあります:

  1. C#で、list<obj>を単一の値として分割し、2つのパラメーター(ID, str)を受け取るストアドプロシージャを作成します。C#は、リストのサイズに応じて、ストアドプロシージャを何度も呼び出します。

  2. IDおよびlist<obj>を2つのパラメーターとしてストアドプロシージャに渡し、ストアドプロシージャにlist<obj>を分割させます。このように、C#はストアドプロシージャを1回だけ呼び出します

2番目の戦略は実行可能ですか?はいの場合、パフォーマンスの点でどちらが優れていますか?

5
user3656901

#2は非常に近いですが、これら2つのオプションのいずれも必要ありません。オプション#1は、行ごとおよびであり、各呼び出しが独自の個別のトランザクションであるために遅くなります。すべてのストアドプロシージャ呼び出しを1つの明示的なトランザクションにラップしたとしても、セットベースのアプローチよりも遅くなります。

オプション#2は、主に両方のレイヤーで行われている追加の作業に欠陥があります。コレクションをアプリレイヤーで単一の文字列に結合し、文字列をデータレイヤーの要素に分割します。ただし、このアイデアの一般的な構造(IDと文字列のコレクションの2つのパラメーターのみを渡す)が適切です。テーブル値パラメーター(TVP)を使用するだけで、コレクションをそのまま反復して、SQL Serverの事前入力されたテーブル変数として受け取ることができます。

これを行うには、ユーザー定義のテーブルタイプ(ここでは単一のNVARCHAR列にすることができます)を作成し、それをコレクションのパラメーターとして使用します(したがって、「テーブル」-「値」「パラメーター」)。

このパズルのもう1つのピースは、アプリのコード側で、_int ID, List<String> _listobj_を受け入れて_IEnumerable<SqlDataRecord>_を返すメソッドを作成し、そのメソッドをSqlParameterの「値」として使用することです。 TVPにマッピングされます。意味、次のようなもの:

_private static IEnumerable<SqlDataRecord> SendRows(List<String> RowData)
{
   SqlMetaData[] _TvpSchema = new SqlMetaData[] {
      new SqlMetaData("SomeSomething", SqlDbType.NVarChar, 4000)
   };
   SqlDataRecord _DataRecord = new SqlDataRecord(_TvpSchema);

   foreach (string _CurrentRow in RowData)
   {
      _DataRecord.SetString(0, _CurrentRow);

      yield return _DataRecord;
   }
}
_

次に、SendRows(_listobj)をTVPパラメータの値として渡すだけです。 SqlCommandを実行すると、そのメソッドが呼び出され、そのコレクションがSQL Serverにストリーミングされます。

上記の手法は、追加のメモリや処理を必要とせず、コレクションをコレクションとして送信するだけです。 DataTablesまたは_String.Join_ ingまたは分割する必要はありません:-)。

このための完全なコード例の設定を確認するには、次のスタックオーバーフローの質問に対する回答を参照してください: ストアドプロシージャT-SQLへの辞書の受け渡し

3
Solomon Rutzky

2番目の方法は実行可能です。カンマ区切りの文字列をSPに渡し、SP内で次のようなカスタムSQLテーブル値関数を使用することができます。

CREATE FUNCTION [dbo].[fn_Split](@text VARCHAR(MAX), @delimiter VARCHAR(5) = ',')

RETURNS @Strings TABLE
(    
    position int IDENTITY PRIMARY KEY,
    value VARCHAR(8000)  
)

AS
BEGIN
    DECLARE @index int 
    SET @index = -1 

    WHILE (LEN(@text) > 0) 
        BEGIN  
            SET @index = CHARINDEX(@delimiter , @text)  

            IF (@index = 0) AND (LEN(@text) > 0)  
                BEGIN   
                    INSERT INTO @Strings VALUES (@text)
                    BREAK  
                END  

            IF (@index > 1)
                BEGIN
                    INSERT INTO @Strings VALUES (LEFT(@text, @index - 1))
                END

            SET @text = RIGHT(@text, (LEN(@text) - (@index+LEN(@delimiter)-1))) 
        END
    RETURN
END

または、follなどのカスタムテーブルタイプを定義し、SqlDbType.Structuredを使用して、テーブル変数を2番目のパラメータとして渡して、C#からSPを呼び出すことができます。

CREATE TYPE [dbo].[MyCustomType] AS TABLE(
Id int not null
)