51

データベース内の特定のレコードに複数のアイテムをロードしなければならないことがよくありました。例: Web ページには、1 つのレポートに含めるアイテムが表示されます。これらはすべてデータベース内のレコードです (レポートはレポート テーブル内のレコードであり、アイテムはアイテム テーブル内のレコードです)。ユーザーは Web アプリを介して 1 つのレポートに含める項目を選択しており、3 つの項目を選択して送信するとします。このプロセスでは、ReportItems (ReportId,ItemId) というテーブルにレコードを追加することで、これら 3 つのアイテムをこのレポートに追加します。

現在、コードで次のようなことを行います。

public void AddItemsToReport(string connStr, int Id, List<int> itemList)
{
    Database db = DatabaseFactory.CreateDatabase(connStr);

    string sqlCommand = "AddItemsToReport"
    DbCommand dbCommand = db.GetStoredProcCommand(sqlCommand);

    string items = "";
    foreach (int i in itemList)
        items += string.Format("{0}~", i);

    if (items.Length > 0)
        items = items.Substring(0, items.Length - 1);

    // Add parameters
    db.AddInParameter(dbCommand, "ReportId", DbType.Int32, Id);
    db.AddInParameter(dbCommand, "Items", DbType.String, perms);
    db.ExecuteNonQuery(dbCommand);
}

そしてこれはストアドプロシージャで:

INSERT INTO ReportItem (ReportId,ItemId)
SELECT  @ReportId,
          Id
FROM     fn_GetIntTableFromList(@Items,'~')

関数は整数の 1 列のテーブルを返します。

私の質問はこれです: このようなものを処理するためのより良い方法はありますか? データベースの正規化などについて質問しているわけではないことに注意してください。私の質問は特にコードに関連しています。

4

8 に答える 8

35

SQL Server 2008 への移行が選択肢の 1 つであれば、まさにこの問題を解決する「テーブル値パラメーター」と呼ばれる新機能があります。

ここここで TVP の詳細を確認するか、「SQL Server 2008 テーブル値パラメーター」について Google に問い合わせてください。多くの情報とサンプルが見つかります。

強くお勧めします - SQL Server 2008 に移行できる場合...

于 2009-01-21T20:48:47.787 に答える
19

文字列結合ロジックはおそらく単純化できます。

string items = 
    string.Join("~", itemList.Select(item=>item.ToString()).ToArray());

これにより、.Net では高価な文字列連結を節約できます。

アイテムの保存方法に問題はないと思います。データベースへの旅行を制限していますが、これは良いことです。データ構造が int のリストよりも複雑な場合は、XML をお勧めします。

注:コメントで、これにより文字列の連結が節約されるかどうか尋ねられました(実際にそうです)。素晴らしい質問だと思いますので、フォローアップしたいと思います。

開いた string.Join with Reflectorをはがすと、Microsoft が char ポインタや UnSafeCharBuffer と呼ばれる構造体の使用など、安全でない (.Net の意味で) いくつかの手法を使用していることがわかります。彼らがしていることは、実際に要約すると、ポインターを使用して空の文字列を横切って結合を構築することです。.Net で文字列の連結が非常にコストがかかる主な理由は、文字列が不変であるため、連結ごとに新しい文字列オブジェクトがヒープに配置されることです。これらのメモリ操作は高価です。String.Join(..) は基本的にメモリを一度割り当ててから、ポインタで操作します。とても早い。

于 2008-10-16T18:28:31.333 に答える
8

この手法の潜在的な問題の 1 つは、非常に大きなリストを処理できないことです。データベースの最大文字列長を超える可能性があります。整数値を文字列の列挙に連結するヘルパー メソッドを使用します。各文字列は指定された最大値よりも小さくなります (次の実装では、オプションで重複 ID をチェックして削除します)。

public static IEnumerable<string> ConcatenateValues(IEnumerable<int> values, string separator, int maxLength, bool skipDuplicates)
{
    IDictionary<int, string> valueDictionary = null;
    StringBuilder sb = new StringBuilder();
    if (skipDuplicates)
    {
        valueDictionary = new Dictionary<int, string>();
    }
    foreach (int value in values)
    {
        if (skipDuplicates)
        {
            if (valueDictionary.ContainsKey(value)) continue;
            valueDictionary.Add(value, "");
        }
        string s = value.ToString(CultureInfo.InvariantCulture);
        if ((sb.Length + separator.Length + s.Length) > maxLength)
        {
            // Max length reached, yield the result and start again
            if (sb.Length > 0) yield return sb.ToString();
            sb.Length = 0;
        }
        if (sb.Length > 0) sb.Append(separator);
        sb.Append(s);
    }
    // Yield whatever's left over
    if (sb.Length > 0) yield return sb.ToString();
}

次に、次のように使用します。

using(SqlCommand command = ...)
{
    command.Connection = ...;
    command.Transaction = ...; // if in a transaction
    SqlParameter parameter = command.Parameters.Add("@Items", ...);
    foreach(string itemList in ConcatenateValues(values, "~", 8000, false))
    {
        parameter.Value = itemList;
        command.ExecuteNonQuery();
    }
}
于 2008-10-16T19:21:55.687 に答える
5

すでに取得していることを実行し、区切り文字列を渡してからテーブル値に解析するか、または他の選択肢として XML の wedge を渡しますが、ほとんど同じです。

http://weblogs.asp.net/jgalloway/archive/2007/02/16/passing-lists-to-sql-server-2005-with-xml-parameters.aspx

SQL 2008 を調べて、この種のことを処理するための新しい機能が追加されているかどうかを確認する機会はまだありません。

于 2008-10-16T18:25:54.940 に答える
5

テーブル値パラメーターを使用しないのはなぜですか? https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql/table-valued-parameters

于 2010-06-11T20:06:40.383 に答える
2

sqlteam.com のテーブル値パラメーターの非常に明確な説明を次に示します。 テーブル値パラメーター

于 2010-09-28T14:24:38.240 に答える
2

この問題の詳細と使用できるさまざまなアプローチについては、http://www.sommarskog.se/arrays-in-sql-2005.htmlを参照してください。

于 2008-10-16T19:26:59.810 に答える
1

ストアド プロシージャ内の複数の値に対して 1 つのフィールドをクエリ
する.aspx

于 2008-10-16T23:04:10.357 に答える