次のように宣言されたEntry
という名前のクラスがあります。
class Entry{
string Id {get;set;}
string Name {get;set;}
}
そして、ADO.NETを使用してデータベースに挿入するために、複数のEntry
オブジェクトを受け入れるメソッド:
static void InsertEntries(IEnumerable<Entry> entries){
//build a SqlCommand object
using(SqlCommand cmd = new SqlCommand()){
...
const string refcmdText = "INSERT INTO Entries (id, name) VALUES (@id{0},@name{0});";
int count = 0;
string query = string.Empty;
//build a large query
foreach(var entry in entries){
query += string.Format(refcmdText, count);
cmd.Parameters.AddWithValue(string.Format("@id{0}",count), entry.Id);
cmd.Parameters.AddWithValue(string.Format("@name{0}",count), entry.Name);
count++;
}
cmd.CommandText=query;
//and then execute the command
...
}
}
そして、私の質問はこれです:複数の挿入ステートメントを送信する上記の方法を使用し続ける(挿入ステートメントとそのパラメーターの巨大な文字列を構築してネットワーク経由で送信する)か、開いた接続を維持して単一の挿入ステートメントを送信する必要があります次のような各Entry
に対して:
using(SqlCommand cmd = new SqlCommand(){
using(SqlConnection conn = new SqlConnection(){
//assign connection string and open connection
...
cmd.Connection = conn;
foreach(var entry in entries){
cmd.CommandText= "INSERT INTO Entries (id, name) VALUES (@id,@name);";
cmd.Parameters.AddWithValue("@id", entry.Id);
cmd.Parameters.AddWithValue("@name", entry.Name);
cmd.ExecuteNonQuery();
}
}
}
どう思いますか? 2つの間にSQL Serverのパフォーマンスの違いはありますか?他に注意すべき結果はありますか?
私があなただったら、どちらも使いません。
最初の欠点は、リストに同じ値がある場合にパラメーター名が衝突する可能性があることです。
2番目の欠点は、各エンティティのコマンドとパラメーターを作成していることです。
最良の方法は、コマンドテキストとパラメーターを1回作成することです(Parameters.Add
は、パラメータを追加するために)ループ内で値を変更し、コマンドを実行します。そうすれば、ステートメントは1回だけ準備されます。また、ループを開始する前に接続を開き、ループを終了する必要があります。
static void InsertSettings(IEnumerable<Entry> settings) {
using (SqlConnection oConnection = new SqlConnection("Data Source=(local);Initial Catalog=Wip;Integrated Security=True")) {
oConnection.Open();
using (SqlTransaction oTransaction = oConnection.BeginTransaction()) {
using (SqlCommand oCommand = oConnection.CreateCommand()) {
oCommand.Transaction = oTransaction;
oCommand.CommandType = CommandType.Text;
oCommand.CommandText = "INSERT INTO [Setting] ([Key], [Value]) VALUES (@key, @value);";
oCommand.Parameters.Add(new SqlParameter("@key", SqlDbType.NChar));
oCommand.Parameters.Add(new SqlParameter("@value", SqlDbType.NChar));
try {
foreach (var oSetting in settings) {
oCommand.Parameters["@key"].Value = oSetting.Key;
oCommand.Parameters["@value"].Value = oSetting.Value;
if (oCommand.ExecuteNonQuery() != 1) {
//'handled as needed,
//' but this snippet will throw an exception to force a rollback
throw new InvalidProgramException();
}
}
oTransaction.Commit();
} catch (Exception) {
oTransaction.Rollback();
oConnection.Close();
throw;
}
}
}
}
}
巨大なコマンドを作成するのではなく、すべてのループでコマンドを実行する必要がありますText(btw、 StringBuilder is this)対応しろ。詳細については、次のリンクをご覧ください。 ASP.NETアプリケーションでのADO.NET接続プーリングの調整
すべてのコマンドが正常に実行されるようにする場合は、 トランザクション と必要に応じてロールバックを使用できます。
エントリが多い場合は、 SqlBulkCopy の使用を検討してください。パフォーマンスは、一連の単一挿入よりもはるかに高速です。
@Tim Mahyのフォローアップ-SqlBulkCopyをフィードするには、DataReaderまたはDataTableの2つの方法があります。 DataTableのコードは次のとおりです。
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("Id", typeof(string)));
dt.Columns.Add(new DataColumn("Name", typeof(string)));
foreach (Entry entry in entries)
dt.Rows.Add(new string[] { entry.Id, entry.Name });
using (SqlBulkCopy bc = new SqlBulkCopy(connection))
{ // the following 3 lines might not be neccessary
bc.DestinationTableName = "Entries";
bc.ColumnMappings.Add("Id", "Id");
bc.ColumnMappings.Add("Name", "Name");
bc.WriteToServer(dt);
}
DataTable
が正しく作成されていれば、直接挿入できます。
最初に、アクセステーブルの列が同じ列名と類似したタイプを持っていることを確認します。次に、この機能を使用できます。この機能は非常に高速でエレガントです。
public void AccessBulkCopy(DataTable table)
{
foreach (DataRow r in table.Rows)
r.SetAdded();
var myAdapter = new OleDbDataAdapter("SELECT * FROM " + table.TableName, _myAccessConn);
var cbr = new OleDbCommandBuilder(myAdapter);
cbr.QuotePrefix = "[";
cbr.QuoteSuffix = "]";
cbr.GetInsertCommand(true);
myAdapter.Update(table);
}