Библиотеки для ADO.NET для быстрой массовой вставки данных в базу данных из файла .csv? - PullRequest
9 голосов
/ 19 февраля 2011

Я хотел бы знать, можете ли вы порекомендовать какие-либо расширенные библиотеки ADO.NET для работы с базами данных.

Я обнаружил, что LINQ-to-Entities отлично подходит для извлечения данных из баз данных, но совсем не полезен для вставки данных в базы данных. Его недостающие функции, такие как быстрая массовая вставка, выборка дубликатов, и большинство расширенных функций, которые вы можете получить с помощью чистого SQL.

Итак: можете ли вы порекомендовать некоторые библиотеки ADO.NET, которые предлагают расширенные функциональные возможности, которых нет в LINQ-to-Entities?

Ответы [ 2 ]

17 голосов
/ 19 февраля 2011

Класс ADO.net SqlBulkCopy обеспечивает быструю массовую загрузку записей в таблицу:

    DataTable dt = s_EmptyUploadTable.Copy();
    foreach (var itm in yourList) {
        DataRow row = dt.NewRow();
        row["Field1"] = itm.Field1;
        row["Field2"] = itm.Field2;
        dt.Rows.Add(row);
    }
    using (SqlConnection cn = new SqlConnection(yourConnectionString)) {
        cn.Open();
        using (SqlBulkCopy bulkCopy = new SqlBulkCopy(cn)) {
            bulkCopy.DestinationTableName = "dbo.YourActualSQLServerTableName";
            bulkCopy.WriteToServer(dt);
        }
        cn.Close();
    }
3 голосов
/ 20 февраля 2011

Вы можете использовать LINQ Entity Data Reader , чтобы записать список IEnumerable в базу данных, используя SQL Bulk Copy за кулисами.Вы можете использовать эту библиотеку для массовой загрузки результатов запроса LINQ прямо в базу данных, поскольку результаты запроса LINQ являются IEnumerable.

Поскольку существуют адаптеры LINQ-to-everything, вы можете выполнять такие приемы, какиспользуйте библиотеку LINQ to CSV , чтобы извлечь данные из CSV-файла с помощью запроса LINQ, затем LINQ Entity Data Reader для массовой записи этих данных непосредственно в базу данных.

Пример:

Проблема: быстро прочитать файл .csv в базу данных.Соединение с базой данных SQL осуществляется через LINQ-to-Entitys из C #.

Решение 1: Использование LINQ to CSV library , создание запроса LINQ для извлеченияданные, которые вы хотите, затем запишите их, используя стандартные вызовы LINQ-to-Entity (ctx.AddObject (), ctx.SaveChanges () и т. д.).Потребовалось время: 30 секунд для 20 000 записей, поскольку LINQ заканчивает генерацией запроса для каждой отдельной записи (slooooow !!!!!).

Решение 2: Используйте LINQ to CSV library , создайте запрос LINQ для извлечения необходимых данных в IEnumerable, используйте LINQ Entity Data Reader , чтобы массово записать эти данные непосредственно в целевую таблицу данных.Требуемое время: 3 секунды для 20 000 записей.

Решение 3: Использование хранимой процедуры с SQL-копированием.Время: 2 секунды для 20 000 записей.Однако это решение довольно хрупкое, поскольку оно опирается на хранимую процедуру, а массовое копирование SQL просто несовместимо с некоторыми форматами файлов .csv.Этот метод также требует, чтобы вы использовали промежуточную таблицу между фактической целевой таблицей и файлом .csv, чтобы справиться с проблемами форматирования файлов и помочь с нормализацией.

И вот исходный код для решения № 2:

static void WriteCSVtoSQLtable()
{
  // Step 1: Read .csv file into IEnumerable using LINQ-to-CSV class.

  // This section requires "LINQtoCSV" class as described at http://www.codeproject.com/KB/linq/LINQtoCSV.asp

  string inputFilePath = @"T:\x.csv";

  CsvFileDescription inputFileDescription = new CsvFileDescription
  {
    SeparatorChar = ',',
    FirstLineHasColumnNames = true
  };

  IEnumerable<MyCustomColumnMappingClass> csvChains = cc.Read<MyCustomColumnMappingClass>(inputFilePath, inputFileDescription);

  // Step 2: Now write into the target table on SQL Server.

  // This section requires "EntityDataReader" class described at http://archive.msdn.microsoft.com/LinqEntityDataReader.
  public static string dbSqlConnectionString = @";Data Source=(local);Initial Catalog=PhiEngine;Integrated Security=True;MultipleActiveResultSets=True";

  SqlConnection dbSql(dbSqlConnectionString);

  using (var tran = dbSql.BeginTransaction())
  {

    var csvFile = from p in csvChains
                select p;

    SqlBulkCopy bc = new SqlBulkCopy(dbSql,
      SqlBulkCopyOptions.CheckConstraints |
      SqlBulkCopyOptions.FireTriggers |
      SqlBulkCopyOptions.KeepNulls, tran)
                     {
                       BatchSize = 1000,
                       DestinationTableName = "TStagingTable" // Temporary staging table in database.
                     };

    bc.WriteToServer(csvFile.AsDataReader()); // Extension method .AsDataReader depends on adding the EntityDataReader class to your C# project (see above).

    tran.Commit();
  }
}

// This class is used by LINQ to CSV to query the .csv file, see "LINQtoCSV" website.
public class MyCustomColumnMappingClass
{
  [CsvColumn(Name = "symbol", FieldIndex = 1)]
  public string Symbol { get; set; }

  [CsvColumn(Name = "date", FieldIndex = 3, OutputFormat = @"MM/dd/yyyy")]
  public DateTime Date { get; set; }
}
...