SQL Вставить одну строку или несколько строк данных? - PullRequest
9 голосов
/ 21 июня 2009

Я работаю над консольным приложением для вставки данных в базу данных MS SQL Server 2005. У меня есть список объектов для вставки. Здесь я использую класс Employee в качестве примера:

List<Employee> employees;

Что я могу сделать, это вставить один объект за раз, как это:

foreach (Employee item in employees)
{
  string sql = @"INSERT INTO Mytable (id, name, salary) 
    values ('@id', '@name', '@salary')";
  // replace @par with values
  cmd.CommandText = sql; // cmd is IDbCommand
  cmd.ExecuteNonQuery();
}

Или я могу построить запрос вставки balk следующим образом:

string sql = @"INSERT INTO MyTable (id, name, salary) ";
int count = employees.Count;
int index = 0;
foreach (Employee item in employees)
{
   sql  = sql + string.format(
     "SELECT {0}, '{1}', {2} ",
     item.ID, item.Name, item.Salary);
   if ( index != (count-1) )
      sql = sql + " UNION ALL ";
   index++
 }
 cmd.CommandType = sql;
 cmd.ExecuteNonQuery();

Полагаю, в последнем случае будут вставлены строки данных сразу. Однако, если у меня есть несколько тысяч данных, есть ли предел для строки запроса SQL?

Я не уверен, что одна вставка с несколькими строками лучше, чем одна вставка с одной строкой данных, с точки зрения производительности?

Какие-нибудь предложения, чтобы сделать это лучше?

Ответы [ 5 ]

14 голосов
/ 21 июня 2009

На самом деле, как вы написали, ваш первый вариант будет быстрее.

  1. В вашем втором примере есть проблема. Вы делаете sql = + sql + и т. Д. Это приведет к созданию нового строкового объекта для каждой итерации цикла. (Проверьте класс StringBuilder). Технически, вы также собираетесь создавать новый строковый объект в первом экземпляре, но разница в том, что ему не нужно копировать всю информацию из предыдущей строковой опции.

  2. Как вы настроили, SQL Server, возможно, придется оценивать массивный запрос, когда вы, наконец, отправите его, что определенно займет некоторое время, чтобы выяснить, что он должен делать. Я должен заявить, что это зависит от того, какое количество вставок вам нужно сделать. Если n мало, вы, вероятно, будете в порядке, но по мере роста ваша проблема будет только ухудшаться.

Массовая вставка выполняется быстрее, чем отдельные, благодаря тому, как SQL-сервер обрабатывает пакетные транзакции. Если вы собираетесь вставлять данные из C #, вы должны выбрать первый подход и обернуть, скажем, каждые 500 вставок в транзакцию и зафиксировать ее, затем выполнить следующие 500 и так далее. Это также имеет то преимущество, что в случае сбоя пакета вы можете перехватить их и выяснить, что пошло не так, и заново вставить только эти. Есть и другие способы сделать это, но это, безусловно, будет лучше, чем два приведенных примера.

var iCounter = 0;
foreach (Employee item in employees)
{

   if (iCounter == 0)
  {
    cmd.BeginTransaction;
  }
  string sql = @"INSERT INTO Mytable (id, name, salary) 
    values ('@id', '@name', '@salary')";
  // replace @par with values
  cmd.CommandText = sql; // cmd is IDbCommand
  cmd.ExecuteNonQuery();
  iCounter ++;
  if(iCounter >= 500)
  {
     cmd.CommitTransaction;
     iCounter = 0;
  }
}

if(iCounter > 0)
   cmd.CommitTransaction;
3 голосов
/ 11 июля 2009

В MS SQL Server 2008 вы можете создать таблицу .Net table-UDT, которая будет содержать вашу таблицу

CREATE TYPE MyUdt AS TABLE (Id int, Name nvarchar(50), salary int)

тогда вы можете использовать этот UDT в своих хранимых процедурах и ваш с # -код для пакетной вставки. SP:

CREATE PROCEDURE uspInsert
 (@MyTvp AS MyTable READONLY)
AS
     INSERT INTO [MyTable]
      SELECT * FROM @MyTvp

C # (представьте, что записи, которые вам нужно вставить, уже содержатся в таблице «MyTable» DataSet ds):

using(conn)
{
    SqlCommand cmd = new SqlCommand("uspInsert", conn);
    cmd.CommandType = CommandType.StoredProcedure;

    SqlParameter myParam = cmd.Parameters.AddWithValue
     ("@MyTvp", ds.Tables["MyTable"]);

    myParam.SqlDbType = SqlDbType.Structured;
    myParam.TypeName = "dbo.MyUdt";

    // Execute the stored procedure
    cmd.ExecuteNonQuery();
}

Итак, это решение.

Наконец, я хочу запретить вам использовать код, подобный вашему (создание строк, а затем выполнение этой строки), поскольку этот способ выполнения может использоваться для SQL-инъекций.

0 голосов
/ 21 июня 2009

Массовое копирование обычно выполняется быстрее, чем самостоятельная вставка.

Если вы все еще хотите сделать это одним из предложенных вами способов, вы должны сделать это так, чтобы вы могли легко изменять размер запросов, отправляемых на сервер. Таким образом вы сможете оптимизировать скорость в вашей производственной среде позже. Время запроса может варьироваться в зависимости от размера запроса.

0 голосов
/ 21 июня 2009

Размер пакета для запроса SQL Server указан как 65 536 * размер сетевого пакета. Размер сетевого пакета по умолчанию составляет 4 КБ, но его можно изменить. Проверьте Максимальная емкость e для SQL 2008, чтобы получить область. SQL 2005 также имеет тот же предел.

0 голосов
/ 21 июня 2009

посмотрите на эту тему , Я ответил там о табличном параметре.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...