web-dev-qa-db-ja.com

forループを使用してSQLServerデータベースに複数のレコードを挿入する

私はC#でWindowsフォームプロジェクトに取り組んでいます。配列からSQLServerデータベースに複数のレコードを挿入しようとしています。

最初の行を入力した後、例外が発生します

@UserIDはすでに宣言されています。変数名は、クエリバッチまたはストアドプロシージャ内で一意である必要があります。

UserIDは主キーではないため、データベースの主キーに問題はありません。

これが私がやろうとしていることです。

public static void featuresentry()
{
    SqlConnection connection = new SqlConnection(HandVeinPattern.Properties.Settings.Default.HandVeinPatternConnectionString);

    SqlCommand command = new SqlCommand();
    connection.Open();

    try
    {
        command = connection.CreateCommand();

        for (int i = 0; i < Details.modelKeyPoints.Size; i++)
        {
            command.CommandText = "INSERT INTO FEATURES(UserID, Angle, ClassID, Octave, PointX, PointY, Response, Size) VALUES(@UserID, @Angle, @ClassID, @Octave, @PointX, @PointY, @Response, @Size)";

            command.Parameters.AddWithValue("@UserID", Details.ID);
            command.Parameters.AddWithValue("@Angle", Convert.ToDouble(Details.modelKeyPoints[i].Angle));
            command.Parameters.AddWithValue("@ClassID", Convert.ToDouble(Details.modelKeyPoints[i].ClassId));
            command.Parameters.AddWithValue("@Octave", Convert.ToDouble(Details.modelKeyPoints[i].Octave));
            command.Parameters.AddWithValue("@PointX", Convert.ToDouble(Details.modelKeyPoints[i].Point.X));
            command.Parameters.AddWithValue("@PointY", Convert.ToDouble(Details.modelKeyPoints[i].Point.Y));
            command.Parameters.AddWithValue("@Response", Convert.ToDouble(Details.modelKeyPoints[i].Response));
            command.Parameters.AddWithValue("@Size", Convert.ToDouble(Details.modelKeyPoints[i].Size));

            command.ExecuteNonQuery();
        }
    }
    catch (Exception)
    {
        throw;
    }
    finally
    {
        if (connection.State == ConnectionState.Open)
        {
            connection.Close();
        }
    }
}
5
Junaid Sultan

あなたはこれをするべきです適切に

  • パラメータを定義するonceループの外側
  • 各反復のループ内のパラメーターのを定義します
  • using(...) { ... }ブロックを使用してtry ... catch ... finallyを削除します(usingブロックは、不要になったときにクラスを適切かつ迅速に破棄します)
  • 例外を実際に処理していない場合は、try...catchの使用を停止してください-例外を再スローするだけです(意味がありません)

このコードを試してください:

public static void featuresentry()
{
    string connectionString = HandVeinPattern.Properties.Settings.Default.HandVeinPatternConnectionString;
    string insertQuery = "INSERT INTO FEATURES(UserID, Angle, ClassID, Octave, PointX, PointY, Response, Size) VALUES(@UserID, @Angle, @ClassID, @Octave, @PointX, @PointY, @Response, @Size)";

    using (SqlConnection connection = new SqlConnection(connectionString))
    using (SqlCommand command = new SqlCommand(insertQuery, connection))
    {
        // define your parameters ONCE outside the loop, and use EXPLICIT typing
        command.Parameters.Add("@UserID", SqlDbType.Int);
        command.Parameters.Add("@Angle", SqlDbType.Double);
        command.Parameters.Add("@ClassID", SqlDbType.Double);
        command.Parameters.Add("@Octave", SqlDbType.Double);
        command.Parameters.Add("@PointX", SqlDbType.Double);
        command.Parameters.Add("@PointY", SqlDbType.Double);
        command.Parameters.Add("@Response", SqlDbType.Double);
        command.Parameters.Add("@Size", SqlDbType.Double);

        connection.Open();

        for (int i = 0; i < Details.modelKeyPoints.Size; i++)
        {
            // now just SET the values
            command.Parameters["@UserID"].Value = Details.ID;
            command.Parameters["@Angle"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Angle);
            command.Parameters["@ClassID"].Value = Convert.ToDouble(Details.modelKeyPoints[i].ClassId);
            command.Parameters["@Octave"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Octave);
            command.Parameters["@PointX"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Point.X);
            command.Parameters["@PointY"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Point.Y);
            command.Parameters["@Response"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Response);
            command.Parameters["@Size"].Value = Convert.ToDouble(Details.modelKeyPoints[i].Size);

            command.ExecuteNonQuery();
        }
    }
}
6
marc_s

Forループ内にcommand = connection.CreateCommand();を入れると、機能します。問題は、コマンドパラメータのみをループしているため、既存のコマンドにさらにパラメータを追加しようとしていることですが、それらはすでに存在しています。したがって、代わりにループごとに新しいコマンドを作成する必要があります。

2
Unicorno Marley

最大のパフォーマンスを得るために、 BulkInsert を検討することができます。これにより、発行されたクエリにはある程度のオーバーヘッドがあるため、挿入が可能な限り高速に実行されます(通常、大きなクエリは多くの小さなクエリよりも高速に実行されます)。次のようになります。

1)AsDataTable拡張メソッドを ここ から定義します:

   public static DataTable AsDataTable<T>(this IEnumerable<T> data)
   {
       PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
       var table = new DataTable();
       foreach (PropertyDescriptor prop in properties)
           table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
       foreach (T item in data)
       {
           DataRow row = table.NewRow();
           foreach (PropertyDescriptor prop in properties)
               row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
           table.Rows.Add(row);
       }
       return table;
   }

2)次のように実際のBulkInsertを実行します(テストされていません):

using (SqlConnection connection = new SqlConnection(connectionString))
{
     connection.Open();
     SqlTransaction transaction = connection.BeginTransaction();

     using (var bulkCopy = new SqlBulkCopy(connection, SqlBulkCopyOptions.Default, transaction))
     {
        bulkCopy.BatchSize = 100;
        bulkCopy.DestinationTableName = "dbo.FEATURES";
        try
        {
            // define mappings for columns, as property names / generated data table column names
            // is different from destination table column name
            bulkCopy.ColumnMappings.Add("ID","UserID");
            bulkCopy.ColumnMappings.Add("Angle","Angle");
            // the other mappings come here

            bulkCopy.WriteToServer(Details.modelKeyPoints.AsDataTable());
        }
        catch (Exception)
        {
            transaction.Rollback();
            connection.Close();
        }
      }

      transaction.Commit();
}

もちろん、 設定より規約 が使用される場合(オブジェクトプロパティ名は宛先テーブルの列名と正確に一致します)、マッピングは必要ありません。

0
Alexei

これを行うには、データをxml文字列として送信し、SQLのストアドプロシージャでテーブルに変換します。例:SQLテーブルに追加/更新するために複数の行を送信するとします。手順は次のとおりです。

  1. 次のメソッドを使用して、クラスまたはクラスのリストをxml文字列に変換します。

    public static string SerializeObjectToXmlString(object value)
    
              {
              var emptyNamepsaces = new XmlSerializerNamespaces(new[] { 
                                        XmlQualifiedName.Empty });
    
        var serializer = new XmlSerializer(value.GetType());
        var settings = new XmlWriterSettings();
        settings.Indent = true;
        settings.OmitXmlDeclaration = true;
    
        using (var stream = new StringWriter())
        using (var writer = XmlWriter.Create(stream, settings))
        {
            serializer.Serialize(writer, value, emptyNamepsaces);
            return stream.ToString();
        }
    }
    
  2. これで、データベースにデータを送信するときに、クラスオブジェクトをxml文字列に変換します(ここでは、コードでエンティティフレームワークを使用していますが、使用せずにこれを行うこともできます)。

    bool AddUpdateData(List<MyClass> data)
    {
        bool returnResult = false;
        string datatXml = Helper.SerializeObjectToXmlString(data);
        var sqlparam = new List<SqlParameter>()
                     {
       new SqlParameter() { ParameterName = "dataXml", Value = datatXml}
    
                     };
        var result = this.myEntity.Repository<SQL_StoredProc_ComplexType>().ExecuteStoredProc("SQL_StoredProc", sqlparam);
        if (result != null && result.Count() > 0)
        {
            returnResult = result[0].Status == 1 ? true : false;
        }
        return returnResult;
    }
    
  3. 今あなたのSQLコード:

3.1テーブル変数を宣言します。

DECLARE @tableVariableName TABLE
(
    ID INT, Name VARCHAR(20)
)

3.2xml文字列をテーブル変数に挿入します

INSERT INTO @tableVariableName
SELECT 
    Finaldata.R.value ('(ID/text())[1]', 'INT') AS ID, 
    Finaldata.R.value ('(Name/text())[1]', 'VARCHAR(20)') AS Name
FROM @MyInputXmlString.nodes ('//ArrayMyClass/MyClass') AS Finaldata (R)

3.3最後に、このテーブル値をSQLテーブルに挿入します

INSERT INTO MyTable (ID, Name)                  
SELECT ID, Name          
FROM @tableVariableName

これにより、forループを使用してデータベースに何度もアクセスする手間が省けます。

それがあなたを助けることを願っています

0
Pranav Mishra

ループの外側にコマンドパラメータを追加するか、ループの内側でコマンドを宣言する必要があります。

最初のケースでは、次のように各パラメーターの値を更新する必要があります。

oleDbCommand1.Parameters["@UserID"].Value = Details.ID;

そして、新しい値が設定されたら、コマンドを実行します。

0
Bulat