Подтвердить что ты не робот

Массовое обновление в С#

Для ввода огромного количества данных в базу данных я собирал всю информацию о вставке в список и преобразовывал этот список в DataTable. Затем я вставляю этот список в базу данных через SqlBulkCopy.

Когда я отправляю свой сгенерированный список LiMyList, который содержит информацию о всех объемных данных, которые я хочу вставить в базу данных, и передаю ее в мою операцию объемной вставки

InsertData(LiMyList, "MyTable");

Где InsertData есть

 public static void InsertData<T>(List<T> list,string TableName)
        {
                DataTable dt = new DataTable("MyTable");
                clsBulkOperation blk = new clsBulkOperation();
                dt = ConvertToDataTable(list);
                ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.PerUserRoamingAndLocal);
                using (SqlBulkCopy bulkcopy = new SqlBulkCopy(ConfigurationManager.ConnectionStrings["SchoolSoulDataEntitiesForReport"].ConnectionString))
                {
                    bulkcopy.BulkCopyTimeout = 660;
                    bulkcopy.DestinationTableName = TableName;
                    bulkcopy.WriteToServer(dt);
                }
        }    

public static DataTable ConvertToDataTable<T>(IList<T> data)
        {
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T));
            DataTable 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;
        }

Теперь я хочу выполнить операцию обновления, есть ли способ вставить данные, сделанные с помощью SqlBulkCopy для обновления данных в DataBase From С#.Net

4b9b3361

Ответ 1

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

Пример:

public static void UpdateData<T>(List<T> list,string TableName)
{
    DataTable dt = new DataTable("MyTable");
    dt = ConvertToDataTable(list);

    using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["SchoolSoulDataEntitiesForReport"].ConnectionString))
    {
        using (SqlCommand command = new SqlCommand("", conn))
        {
            try
            {
                conn.Open();

                //Creating temp table on database
                command.CommandText = "CREATE TABLE #TmpTable(...)";
                command.ExecuteNonQuery();

                //Bulk insert into temp table
                using (SqlBulkCopy bulkcopy = new SqlBulkCopy(conn))
                {
                    bulkcopy.BulkCopyTimeout = 660;
                    bulkcopy.DestinationTableName = "#TmpTable";
                    bulkcopy.WriteToServer(dt);
                    bulkcopy.Close();
                }

                // Updating destination table, and dropping temp table
                command.CommandTimeout = 300;
                command.CommandText = "UPDATE T SET ... FROM " + TableName + " T INNER JOIN #TmpTable Temp ON ...; DROP TABLE #TmpTable;";
                command.ExecuteNonQuery();
            }
            catch (Exception ex)
            {
                // Handle exception properly
            }
            finally
            {
                conn.Close();
            }
        }
    }
}

Обратите внимание, что для выполнения всей операции используется одно соединение, чтобы иметь возможность использовать временную таблицу на каждом шаге, поскольку объем таблицы temp для каждого соединения.

Ответ 2

В моем личном опыте лучший способ справиться с этой ситуацией - использовать хранимую процедуру с Table-Valued Parameter и a User-Defined Table Type. Просто настройте тип с столбцами таблицы данных и передайте в таблицу данных в качестве параметра в команде SQL.

Внутри хранимой процедуры вы можете либо присоединиться к определенному уникальному ключу (если все строки, которые вы обновляете, существуют), либо - если вы можете столкнуться с ситуацией, когда вам нужно делать как обновления, так и вставки - используйте SQL Merge в хранимой процедуре для обработки как обновлений, так и вставок, как применимо.

В Microsoft есть ссылка на синтаксис и статья с примерами для Merge.

Для части .NET просто установить тип параметра как SqlDbType.Structured и установить значение указанного параметра в таблицу данных, содержащую записи, которые вы хотите обновить.

Этот метод обеспечивает как ясность, так и простоту обслуживания. Хотя могут быть и способы повышения производительности (например, удаление его во временную таблицу, а затем повторение этой таблицы), я думаю, что они перевешиваются простотой позволить .NET и SQL обрабатывать таблицу и обновлять сами записи. K.I.S.S.

Ответ 3

Попробуйте SqlBulkTools, доступные на Nuget.

Отказ от ответственности: я автор этой библиотеки.

var bulk = new BulkOperations();
var records = GetRecordsToUpdate();

using (TransactionScope trans = new TransactionScope())
{
    using (SqlConnection conn = new SqlConnection(ConfigurationManager
    .ConnectionStrings["SqlBulkToolsTest"].ConnectionString))
    {
        bulk.Setup<MyTable>()
            .ForCollection(records)
            .WithTable("MyTable")
            .AddColumn(x => x.SomeColumn1)
            .AddColumn(x => x.SomeColumn2)
            .BulkUpdate()
            .MatchTargetOn(x => x.Identifier)
            .Commit(conn);
    }

    trans.Complete();
}  

Только "SomeColumn1" и "SomeColumn2" будут обновлены. Больше примеров можно найти здесь

Ответ 4

Не уверен, что я понял, что вы собираетесь архивировать... Если ваш вопрос касается быстрой замены всего содержимого таблицы, то я бы пошел на truncate (http://technet.microsoft.com/en-us/library/ms177570. aspx) и массовая вставка новой порции данных. Но этот подход будет работать только в том случае, если у вас нет ограничений внешнего ключа.

Если вы хотите реального обновления, чем искать ответ от Гильермо Гутьеррес.

Ответ 5

Я бы вставлял новые значения во временную таблицу, а затем делал слияние с таблицей назначения, примерно так:

MERGE [DestTable] AS D 
USING #SourceTable S
    ON D.ID = S.ID
WHEN MATCHED THEN 
    UPDATE SET ...
WHEN NOT MATCHED 
THEN INSERT (...) 
VALUES (...);

Ответ 6

Вы можете попытаться создать запрос, содержащий все данные. Используйте case. Это может выглядеть так.

update your_table
set some_column = case when id = 1 then 'value of 1'
                       when id = 5 then 'value of 5'
                       when id = 7 then 'value of 7'
                       when id = 9 then 'value of 9'
                  end
where id in (1,5,7,9)

Ответ 7

Я бы выбрал метод TempTable, потому что таким образом вы ничего не блокируете. Но если ваша логика должна быть только в интерфейсе, и вам нужно использовать массовую копию, я бы попытался применить метод "Удалить/Вставить", но в том же SqlTransaction, чтобы обеспечить целостность, которая была бы примерно такой:

// ...

dt = ConvertToDataTable(list);

using (SqlConnection cnx = new SqlConnection(myConnectionString))
{
    using (SqlTranscation tran = cnx.BeginTransaction())
    {
        DeleteData(cnx, tran, list);

        using (SqlBulkCopy bulkcopy = new SqlBulkCopy(cnx, SqlBulkCopyOptions.Default, tran))
        {
            bulkcopy.BulkCopyTimeout = 660;
            bulkcopy.DestinationTableName = TabelName;
            bulkcopy.WriteToServer(dt);
        }

        tran.Commit();
    }
}

Ответ 8

Массовая операция была бы хорошим способом привести к этому.

Есть репозиторий github, который содержит оба полезных метода: BulkInsert и BulkUpdate с использованием MySql и EF6+.

BulkUpdate/BulkInsert в основном считывает все свойства из вашей общей сущности, а затем создает массовый запрос для вас.

Ps: Это было разработано для моих нужд, и проект открыт для тех, кто хочет улучшить его или изменить его на лучшее решение, которое будет полезно сообществу.

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

Пожалуйста, посмотрите здесь