2013-07-23 1 views
2

Я решил заменить/преобразовать DataTable в IDataReader из-за проблем с памятью.Как создать собственный класс w/IDataReader из LumenWorks (Fast CSV Reader) для использования SqlBulkCopy, имея несколько разных версий файлов?

После долгого времени Google & Поиск в MSDN, я натолкнулся на это в http://www.michaelbowersox.com/2011/12/22/using-a-custom-idatareader-to-stream-data-into-a-database/ и Bulk Insert Sql Server millions of record.

Поскольку я использую LumenWorks Fast CSV Reader, и я еще не понял, как сообщить CsvReader, что IDataReader использует две разные версии полей. :-(CsvReader.FieldCount является ключевым здесь, но я не вижу, как сказать CsvReader использовать один из двух новых классов, имеющих интерфейс IDataReader. См. Исходный скрипт и измененный скрипт ниже ... Спасибо ...

// Оригинальные сценарии ...

var dbConnection = new SqlConnection(_dbConnectionString); 

using (var dbBulkCopy = new SqlBulkCopy(dbConnection) 
{ 
    using (CsvReader csvReader = new CsvReader(new StreamReader(filePath), false, '|', '"', '\\', '#', ValueTrimmingOptions.UnquoteOnly)) 
    { 
     while(csvReader.ReadNextRecord()) 
     { 
      if (csvReader.FieldCount == 48) 
      { 
       //Version 1... 
       dataRow["DealerId"] = csvReader[0]; 
       dataRow["DealerName"] = csvReader[1]; 
       //Etc... 
      } 
      else if (csvReader.FieldCount == 51) 
      { 
       //Version 2... 
       dataRow["DealerId"] = csvReader[0]; 
       dataRow["DealerName"] = csvReader[1]; 
       //Etc... 
      } 
      else { throw new Exception("Field Total Count Mismatched"); } 

      dataTable.Rows.Add(dataRow); 
     } 

     dbConnection.Open(); 

     dbBulkCopy.WriteToServer(dataTable); 
    } 
} 

// Новый скрипт ...

var dbConnection = new SqlConnection(_dbConnectionString); 

using (var dbBulkCopy = new SqlBulkCopy(dbConnection) 
{ 
    dbConnection.Open(); 

    using (CsvReader csvReader = new CsvReader(new StreamReader(filePath), false, '|', '"', '\\', '#', ValueTrimmingOptions.UnquoteOnly)) 
    { 
     csvReader.ReadNextRecord(); 

     dbBulkCopy.WriteToServer(
      if (csvReader.FieldCount == 48) 
      { 
       //Version 1... 

       csvReader....??? //Assign a custom class having IDataTable... 
      } 
      else if (csvReader.FieldCount == 51) 
      { 
       //Version 2... 
       csvReader....??? //Assign a custom class having IDataTable... 
      } 
      else { throw new Exception("Field Total Count Mismatched"); } 
     ); 
    } 
} 

// Пример сценария ...

using (var file = new StreamReader(path)) 
using (var csv = new CsvReader(file, true)) // true = has header row 
using (var bcp = new SqlBulkCopy(connection)) { 
    bcp.DestinationTableName = "TableName"; 
    bcp.WriteToServer(csv); 
} 
+0

Я бы предложил посмотреть @ [это сообщение] (http://stackoverflow.com/questions/7791563/load-very-big-csv-files-into-s-sql-server-database). Чтение csv в таблицах данных, а затем вставка их может быть болезненной. И для вашего конкретного случая у меня когда-то была аналогичная проблема. Файл csv, который я получал, не был форматирован равномерно. Поэтому я добавил шаг 0 перед всеми другими видами лечения, и этот шаг состоял в том, чтобы удалить сильно отформатированные строки и/или переформатировать их должным образом [Вы можете даже разбить файл на создание 2: один для 48 полей, другой - по 51). Затем, используя sqlbulkcopy, вы можете легко переносить данные. –

+0

Я добавил коды «Пример сценария» из предоставленной веб-ссылки. Я не уверен, что такое шаг 0, о котором вы говорите.Предположим, у меня есть 23 файла в папке, и каждый из них запускается по одному за раз. 13 файлов имеют 48 полей и 10 файлов имеют 51 поле по всем строкам. Таблица базы данных содержит 51 поле. У меня не возникло бы проблемы с файлом flat/50 полей, но 48 полей плоского файла, я не вижу, как сказать классу IDataReader использовать значения по умолчанию для 4 отсутствующих полей. – fletchsod

+0

Я написал ответ, так как комментарии не будут предлагать достаточное место. –

ответ

1

Так как это будет немного долго, я пишу это как ответ.

* Я предполагаю, что, несмотря на то, что у вас есть два вида CSV-файл, поступающие с различными заказами на местах, целевая таблица является же. * [EDIT] Нет необходимости предполагать, Вы указали в ваш комментарий.

Чтобы быть в состоянии понять свой контекст, я получил некоторые данные выборки из here.

Допустим, что первый тип файла что-то вроде следующего:

Rk,Year,Age,Tm,Lg,Id,G,W,L,W-L,Finish, 
1,1978,37,Atlanta Braves,NL,,162,69,93,.426,6 

И второй тип, как (некоторые столбцы перепутаны Возраст < -> Готовы и дополнительные поля)

Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3 
1,1978,Very good year,Atlanta Braves,NL,,162,69,93,.426,96,,,, 

Так целевая таблица будет выглядеть сом ething как (только столбцы)

Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3 

Я вижу два варианта (+1 варианта в конце) здесь:

ВАРИАНТ 1

  1. Добавить Шаг 0, чтобы все входные файлы единообразны на полевом уровне, определяя формат поля. Это можно сделать, создав те же поля, которые у вас есть в базе данных.

[Давайте представим, что Test4 и test5 столбцы, которые существуют на целевой таблице, но отсутствующие в обоих файлах CSV]

Rk,Year,Finish,Tm,Lg,Id,G,W,L,W-L,Age,Test1,Test2,Test3,Test4,Test5 
  1. разбирать все файлы, которые вы (оба типа) переписан их на один (или несколько, который зависит от вас) в соответствии с форматом, который вы определили. Таким образом, у вас есть только один (или несколько) файл с уникальным форматом.

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

ВАРИАНТ 2

Вы будете делать SqlBulkCopy операцию дважды. В первом раунде вы будете читать файлы с 48 полями, а в следующем раунде вы будете читать файлы, имеющие 51 поле.

  var FilesWith48Fields = Directory.GetFiles(@"D:\Data\48Fields", "*.csv"); 

      foreach (var fileName in FilesWith48Fields) 
      { 
       using (var file = new StreamReader(fileName)) 
       using (var csv = new CsvReader(file, true)) // true = has header row 
       using (var bcp = new SqlBulkCopy(connection, SqlBulkCopyOptions.KeepNulls)) 
       { 
        bcp.DestinationTableName = "fletchsodTable"; 
        // map the first field of your CSV to the column in Database 
        var mapping1 = new SqlBulkCopyColumnMapping(0, "FirstColumnName"); 
        bcp.ColumnMappings.Add(mapping1); 

        var mapping2 = new SqlBulkCopyColumnMapping(1, "SecondColumnName"); 
        bcp.ColumnMappings.Add(mapping2); 
        .... 

        bcp.WriteToServer(csv); 
       } 
      } 

И Повторите то же самое с файлами, имеющими 51 поля

var FilesWith51Fields = Directory.GetFiles(@"D:\Data\51Fields", "*.csv"); 
...... 

Более подробную информацию о SqlBulkCopyColumnMapping can be found here.

ВАРИАНТ 3

В случае, если вы хотите приключений в создании чтения данных, вот некоторые ссылки:

Daniel Wertheim's blog

A sample implentation on codeproject

Another one

And finally MSDN

ЛИЧНОЕ ПРИМЕЧАНИЕ Из-за нехватки времени для подобной проблемы шахты я отказался от 3-й вариант, потому что со всеми модульных тестов и оптимизации, которые вы должны сделать и еще ухищрений, это может занять некоторое время (по крайней мере, для меня, это был случай)

ВАРИАНТ 4 Возможно, с отображением столбца я указал в ВАРИАНТ 2, вы хотели бы реализовать свой путь путем тестирования счетчиков на местах. Но инстинктивно я предлагаю не считать поля строгими закодированными целыми числами.

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

  • Нет связанных вопросов^_^