2014-02-10 3 views
6

Я знаю, что могу сделать объемную вставку в таблицу со столбцом идентификации, не указав SqlBulkCopyOptions.KeepIdentity, как упомянуто here.Как получить сгенерированные сервера Значения идентичности при использовании SqlBulkCopy

Что я хотел бы сделать, это получить идентификационные значения, которые генерирует сервер, и поместить их в мой datatable или даже список. Я видел сообщение this, но я хочу, чтобы мой код был общим, и я не могу иметь столбец версии во всех моих таблицах. Любые предложения очень ценятся. Вот мой код:

public void BulkInsert(DataTable dataTable, string DestinationTbl, int batchSize) 
{ 
    // Get the DataTable 
    DataTable dtInsertRows = dataTable; 

    using (SqlBulkCopy sbc = new SqlBulkCopy(sConnectStr)) 
    { 
     sbc.DestinationTableName = DestinationTbl; 

     // Number of records to be processed in one go 
     sbc.BatchSize = batchSize; 

     // Add your column mappings here 
     foreach (DataColumn dCol in dtInsertRows.Columns) 
     { 
      sbc.ColumnMappings.Add(dCol.ColumnName, dCol.ColumnName); 
     } 

     // Finally write to server 
     sbc.WriteToServer(dtInsertRows); 
    } 
} 
+0

I сделав это, добавив еще один столбец в таблицу назначения, называемую «OriginalKey», а затем после того, как SqlBulkCopy завершил выбор OriginalKey, NewIdentityId и удалил столбец. Я считаю, что вы также можете использовать SqlBulkCopy для таблицы temp и INSERT с предложением OUTPUT, но теперь вы делаете два шага вместо одного. Или пропустите SqlBulkCopy и используйте TVP в инструкции INSERT ... OUTPUT. –

+0

Рассматривали ли вы создание триггера INSERT в своей таблице и копирование значений идентификатора во временную таблицу, с которой может запрашивать ваш код C#? –

+0

@ ta.speot.is Если у меня есть большая таблица (несколько миллионов записей), какой эффект добавит и опустит столбец на производительность? Будет ли это работать с одновременными процессами, вставляемыми в одну и ту же таблицу? – dseiple

ответ

6

AFAIK, вы не можете.

Единственный способ, которым я знаю, чтобы получить значения (-и) поля идентичности, - либо с помощью SCOPE_IDENTITY() при вставке строки за строкой; или с использованием подхода OUTPUT при вставке всего набора.

«Простейший» подход, вероятно, состоял бы в том, что вы бы SqlBulkCopy записывали в таблицу, а затем снова возвращали обратно. Проблема может заключаться в том, что было бы сложно правильно (и быстро) снова извлечь эти строки с сервера. (Например, это было бы довольно уродливые (и медленно), чтобы иметь пункт WHERE с IN (guid1, guid2, .., guid999998, guid999999) =)

Я предполагаю, что производительность проблема здесь, как вы уже используете SqlBulkCopy, так что я предложил бы пойти на OUTPUT подход, в этом случае вам сначала понадобится промежуточная таблица для SqlBulkCopy ваших записей. В приведенной таблице следует указать какой-то идентификатор партии (GUID?), чтобы позволить нескольким проходам работать бок о бок. Вам понадобится хранимая процедура для INSERT <table> OUTPUT inserted.* SELECT данных из промежуточного стола в фактическую таблицу назначения, а также снова очистите таблицу промежуточного уровня. Набор returend recordset из указанной процедуры затем будет соответствовать 1: 1 исходному набору данных, ответственному за заполнение промежуточной таблицы, но, конечно же, вы не должны полагаться на его порядок. Другими словами: ваш следующий вызов, чем будет соответствовать возвращенным полям Identity обратно к оригиналу записей в вашем приложении.

Думаю, что во всех случаях, кроме строки за строкой & Подход SCOPY_IDENTITY(), который будет медленным с собакой - вам нужно будет (или добавить) a 'key' к вашим данным, чтобы связать сгенерированный идентификатор с исходными данными =/

0

Вы можете сделать аналогичный подход, описанный выше, но вместо того, чтобы возвращать их обратно через WHERE IN (guid1, etc.... Вы сопоставляете их с строки, вставленные в память на основе их порядка.

Поэтому я бы предложил добавить столбец в таблицу для соответствия строке транзакции SqlBulkCopy, а затем выполнить следующее, чтобы совместить сгенерированные идентификаторы обратно в коллекцию памяти только что вставленных строк.

  • Создать новую Guid и установить это значение на всех строк в объемном отображении копирования в новый столбец

  • Запустите WritToServer метод объекта BulkCopy

  • Получить все строки которые имеют тот же самый ключ

  • Перейдите через этот список, который будет в том порядке, в котором они были добавлены, они будут в том же порядке, что и в ячейке памяти в строках, чтобы вы тогда будет знать сгенерированный идентификатор для каждого элемента.

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

List<myObject> myCollection = new List<myObject> 

Guid identifierKey = Guid.NewGuid(); 

//Do your bulk insert where all the rows inserted have the identifierKey 
//set on the new column. In this example you would create a data table based 
//off the myCollection object. 

//Identifier is a column specifically for matching a group of rows to a sql 
//bulk copy command 
var myAddedRows = myDbContext.DatastoreRows.AsNoTracking() 
      .Where(d => d.Identifier == identiferKey) 
      .ToList(); 


for (int i = 0; i < myAddedRows.Count ; i++) 
{ 
    var savedRow = myAddedRows[i]; 
    var inMemoryRow = myCollection[i]; 

    int generatedId = savedRow.Id; 

    //Now you know the generatedId for the in memory object you could set a 
    // a property on it to store the value 

    inMemoryRow.GeneratedId = generatedId; 
} 
+0

Хотя это, вероятно, работает в 99% случаев, я задаюсь вопросом, гарантирует ли SqlBulkCopy его первоначальный порядок при загрузке и/или при отправке данных в РСУБД. Но даже тогда, когда это происходит, ваш выбор затем должен убедиться, что он извлекает значения в порядке полей идентичности. Поскольку ваш текущий запрос не обеспечивает выполнение какого-либо заказа, вы просто запрашиваете набор строк в любом заданном порядке. В зависимости от таблицы-макета вы можете (часто) вернуть данные в порядке поля идентификации, но нет никакой гарантии! После многопоточности пинки вы будете укушены! – deroby

Смежные вопросы