2010-06-04 5 views
22

У меня есть класс с именем Entry объявлен как это:Как мне несколько вставить несколько записей?

class Entry{ 
    string Id {get;set;} 
    string Name {get;set;} 
} 

, а затем метод, который будет принимать несколько таких Entry объектов для вставки в базу данных с помощью ADO.NET:

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 
     ... 
    } 
} 

И мой вопрос заключается в следующем: должен ли я продолжать использовать вышеупомянутый способ отправки нескольких операторов вставки (построить гигантскую строку операторов вставки и их параметры и отправить ее по сети), или я должен сохранить открытое соединение и отправить один оператор insert для каждого как это:

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(); 
     } 
    } 
} 

Как вы относитесь? Будет ли разница в производительности на сервере Sql между этими двумя? Есть ли какие-либо другие последствия, о которых я должен знать?

+0

Спасибо за все ваши предложения! я возьму ответ @ Giorgi, потому что он более или менее отвечает на исходный вопрос – bottlenecked

+0

, вы можете использовать пользовательский тип на сервере SQl для передачи DataTable на SQL-сервер http://www.fourthbottle.com/2014/09/how- to-insert-multiple-records-in.html – Venki

ответ

20

Если бы я тебя, я бы не использовать один из их.

Недостатком первого является то, что имена параметров могут сталкиваться, если в списке есть одинаковые значения.

Недостатком второго является то, что вы создаете команду и параметры для каждого объекта.

Наилучший способ состоит в том, чтобы текст и параметры команды были построены один раз (для добавления параметров) используйте значения Parameters.Add), чтобы изменить их значения в цикле и выполнить команду. Таким образом, заявление будет подготовлено только один раз. Вы также должны открыть соединение, прежде чем запускать цикл и закрыть его после него.

+6

«SQLCommand» оптимизирован для процесса, описанного Джорджи. Основное соединение будет поддерживаться по мере того, как указывает Тим. Я также использовал бы «Транзакцию», рекомендованную Тимом. – AMissico

+0

Итак, также 'cmd.ExecuteNonQuery();' должен находиться внутри цикла? – Ciccio

+1

@Giorgi Что делать, если количество параметров может превышать предел 2100? –

8

Вы должны выполнить команду на каждом цикле, а не строить огромную команду Text (кстати, StringBuilder делается для этого) Базовое соединение не будет закрыть и повторно открыть для каждого цикла, пусть менеджер пул соединений с этим справиться , Посмотрите на эту ссылку для получения более подробной информации: Tuning Up ADO.NET Connection Pooling in ASP.NET Applications

Если вы хотите, чтобы убедиться, что каждая команда будет выполнена успешно можно использовать Transaction и Откат в случае необходимости,

+0

Или лучше использовать Linq2Sql и позволить Linq2Sql справиться с этим. – Amitabh

+2

«SQLCommand» оптимизирован для процесса, описанного Гиорги. Основное соединение будет поддерживаться по мере того, как указывает Тим. Я также использовал бы «Транзакцию», рекомендованную Тимом. – AMissico

39
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[0].Value = oSetting.Key; 
         oCommand.Parameters[1].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(); 
        throw; 
       } 
      } 
     } 
    } 
} 
2

, когда он много записей рассмотреть возможность использования SqlBulkCopy

-1

Хранимая процедура для вставки нескольких записей с помощью одного вставки: за

ALTER PROCEDURE [dbo].[Ins] 
@i varchar(50), 
@n varchar(50), 
@a varchar(50), 
@i1 varchar(50), 
@n1 varchar(50), 
@a1 varchar(50), 
@i2 varchar(50), 
@n2 varchar(50), 
@a2 varchar(50) 
AS 
INSERT INTO t1 
SELECT  @i AS Expr1, @i1 AS Expr2, @i2 AS Expr3 
UNION ALL 
SELECT  @n AS Expr1, @n1 AS Expr2, @n2 AS Expr3 
UNION ALL 
SELECT  @a AS Expr1, @a1 AS Expr2, @a2 AS Expr3 
RETURN 

Код:

protected void Button1_Click(object sender, EventArgs e) 
{ 
    cn.Open(); 
    SqlCommand cmd = new SqlCommand("Ins",cn); 
    cmd.CommandType = CommandType.StoredProcedure; 
    cmd.Parameters.AddWithValue("@i",TextBox1.Text); 
    cmd.Parameters.AddWithValue("@n",TextBox2.Text); 
    cmd.Parameters.AddWithValue("@a",TextBox3.Text); 
    cmd.Parameters.AddWithValue("@i1",TextBox4.Text); 
    cmd.Parameters.AddWithValue("@n1",TextBox5.Text); 
    cmd.Parameters.AddWithValue("@a1",TextBox6.Text); 
    cmd.Parameters.AddWithValue("@i2",TextBox7.Text); 
    cmd.Parameters.AddWithValue("@n2",TextBox8.Text); 
    cmd.Parameters.AddWithValue("@a2",TextBox9.Text); 
    cmd.ExecuteNonQuery(); 
    cn.Close(); 
    Response.Write("inserted"); 
    clear(); 
} 
+6

Что делать, если у меня есть 2 миллиона записей? –

-4
ClsConectaBanco bd = new ClsConectaBanco(); 

StringBuilder sb = new StringBuilder(); 
sb.Append(" INSERT INTO FAT_BALANCETE "); 
sb.Append(" ([DT_LANCAMENTO]   "); 
sb.Append(" ,[ID_LANCAMENTO_CONTABIL] "); 
sb.Append(" ,[NR_DOC_CONTABIL]   "); 
sb.Append(" ,[TP_LANCAMENTO_GERADO] "); 
sb.Append(" ,[VL_LANCAMENTO]   "); 
sb.Append(" ,[TP_NATUREZA]    "); 
sb.Append(" ,[CD_EMPRESA]    "); 
sb.Append(" ,[CD_FILIAL]    "); 
sb.Append(" ,[CD_CONTA_CONTABIL]  "); 
sb.Append(" ,[DS_CONTA_CONTABIL]  "); 
sb.Append(" ,[ID_CONTA_CONTABIL]  "); 
sb.Append(" ,[DS_TRIMESTRE]   "); 
sb.Append(" ,[DS_SEMESTRE]    "); 
sb.Append(" ,[NR_TRIMESTRE]   "); 
sb.Append(" ,[NR_SEMESTRE]    "); 
sb.Append(" ,[NR_ANO]     "); 
sb.Append(" ,[NR_MES]     "); 
sb.Append(" ,[NM_FILIAL])    "); 
sb.Append(" VALUES      "); 
sb.Append(" (@DT_LANCAMENTO   "); 
sb.Append(" ,@ID_LANCAMENTO_CONTABIL "); 
sb.Append(" ,@NR_DOC_CONTABIL   "); 
sb.Append(" ,@TP_LANCAMENTO_GERADO  "); 
sb.Append(" ,@VL_LANCAMENTO   "); 
sb.Append(" ,@TP_NATUREZA    "); 
sb.Append(" ,@CD_EMPRESA    "); 
sb.Append(" ,@CD_FILIAL    "); 
sb.Append(" ,@CD_CONTA_CONTABIL  "); 
sb.Append(" ,@DS_CONTA_CONTABIL  "); 
sb.Append(" ,@ID_CONTA_CONTABIL  "); 
sb.Append(" ,@DS_TRIMESTRE    "); 
sb.Append(" ,@DS_SEMESTRE    "); 
sb.Append(" ,@NR_TRIMESTRE    "); 
sb.Append(" ,@NR_SEMESTRE    "); 
sb.Append(" ,@NR_ANO     "); 
sb.Append(" ,@NR_MES     "); 
sb.Append(" ,@NM_FILIAL)    "); 

SqlCommand cmd = new SqlCommand(sb.ToString(), bd.CriaConexaoSQL()); 
bd.AbrirConexao(); 

cmd.Parameters.Add("@DT_LANCAMENTO", SqlDbType.Date); 
cmd.Parameters.Add("@ID_LANCAMENTO_CONTABIL", SqlDbType.Int); 
cmd.Parameters.Add("@NR_DOC_CONTABIL", SqlDbType.VarChar,255); 
cmd.Parameters.Add("@TP_LANCAMENTO_GERADO", SqlDbType.VarChar,255); 
cmd.Parameters.Add("@VL_LANCAMENTO", SqlDbType.Decimal); 
cmd.Parameters["@VL_LANCAMENTO"].Precision = 15; 
cmd.Parameters["@VL_LANCAMENTO"].Scale = 2; 
cmd.Parameters.Add("@TP_NATUREZA", SqlDbType.VarChar, 1); 
cmd.Parameters.Add("@CD_EMPRESA",SqlDbType.Int); 
cmd.Parameters.Add("@CD_FILIAL", SqlDbType.Int); 
cmd.Parameters.Add("@CD_CONTA_CONTABIL", SqlDbType.VarChar, 255); 
cmd.Parameters.Add("@DS_CONTA_CONTABIL", SqlDbType.VarChar, 255); 
cmd.Parameters.Add("@ID_CONTA_CONTABIL", SqlDbType.VarChar,50); 
cmd.Parameters.Add("@DS_TRIMESTRE", SqlDbType.VarChar, 4); 
cmd.Parameters.Add("@DS_SEMESTRE", SqlDbType.VarChar, 4); 
cmd.Parameters.Add("@NR_TRIMESTRE", SqlDbType.Int); 
cmd.Parameters.Add("@NR_SEMESTRE", SqlDbType.Int); 
cmd.Parameters.Add("@NR_ANO", SqlDbType.Int); 
cmd.Parameters.Add("@NR_MES", SqlDbType.Int); 
cmd.Parameters.Add("@NM_FILIAL", SqlDbType.VarChar, 255); 
cmd.Prepare(); 

foreach (dtoVisaoBenner obj in lista) 
{ 
    cmd.Parameters["@DT_LANCAMENTO"].Value = obj.CTLDATA; 
    cmd.Parameters["@ID_LANCAMENTO_CONTABIL"].Value = obj.CTLHANDLE.ToString(); 
    cmd.Parameters["@NR_DOC_CONTABIL"].Value = obj.CTLDOCTO.ToString(); 
    cmd.Parameters["@TP_LANCAMENTO_GERADO"].Value = obj.LANCAMENTOGERADO; 
    cmd.Parameters["@VL_LANCAMENTO"].Value = obj.CTLANVALORF; 
    cmd.Parameters["@TP_NATUREZA"].Value = obj.NATUREZA; 
    cmd.Parameters["@CD_EMPRESA"].Value = obj.EMPRESA; 
    cmd.Parameters["@CD_FILIAL"].Value = obj.FILIAL; 
    cmd.Parameters["@CD_CONTA_CONTABIL"].Value = obj.CONTAHANDLE.ToString(); 
    cmd.Parameters["@DS_CONTA_CONTABIL"].Value = obj.CONTANOME.ToString(); 
    cmd.Parameters["@ID_CONTA_CONTABIL"].Value = obj.CONTA; 
    cmd.Parameters["@DS_TRIMESTRE"].Value = obj.TRIMESTRE; 
    cmd.Parameters["@DS_SEMESTRE"].Value = obj.SEMESTRE; 
    cmd.Parameters["@NR_TRIMESTRE"].Value = obj.NRTRIMESTRE; 
    cmd.Parameters["@NR_SEMESTRE"].Value = obj.NRSEMESTRE; 
    cmd.Parameters["@NR_ANO"].Value = obj.NRANO; 
    cmd.Parameters["@NR_MES"].Value = obj.NRMES; 
    cmd.Parameters["@NM_FILIAL"].Value = obj.NOME; 
    cmd.ExecuteNonQuery(); 
    rowAffected++; 
} 
+2

, пожалуйста, не отвечайте только на код. – Cybermaxs

+13

Это произведение искусства. – Ashe

+0

Пикассо будет гордиться – Monkey

0

Вы можете напрямую вставить 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); 
} 
0

Вслед Махи @ Тим - Там две возможные пути кормить SqlBulkCopy: а DataReader или через DataTable.Здесь код для 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); 
} 
Смежные вопросы