Как загрузить миллионы строк данных из файла dbf на SQL Server после удаления специальных символов из данных в C#

#c# #asp.net #sql-server #ssis

#c# #asp.net #sql-сервер #ssis

Вопрос:

Моя задача — загрузить .dbf файл на SQL Server после удаления специальных символов, присутствующих в записях.

Я использовал регулярное выражение для удаления специального символа. Сначала данные загружаются в устройство чтения данных, а затем загружаются в таблицу данных.

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

Он отлично работает для 150000 строк, но для 3 миллионов строк я получаю OutOfMemoryException , пока данные загружаются в datatable.

     DataTable dt = new DataTable();

    using (OleDbDataReader dr = cmd.ExecuteReader())
    {
        //Logic to Remove Special Characters from the file.
        dt.Load(dr); //OutOfMemoryException occurred here

        foreach (DataRow row in dt.Rows)
        {
            for (int i = 0; i < dt.Columns.Count; i  )
            {
                if (dt.Columns[i].DataType == typeof(string))
                    row[i] = Regex.Replace(row[i].ToString(), "[#$%^*@!~?]", "");
            }
        }

        dt.AcceptChanges();

        // Bulk Copy to SQL Server
        using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn))
        {
             // code for bulk insert
             bulkCopy.WriteToServer(dt);
        }
    }
  

Комментарии:

1. я предлагаю вам использовать SSIS для этого. Это очень быстро и просто в использовании

2. И как веб-пользователи, загружающие файл, собираются это сделать? Теперь вы можете использовать SSIS Shell (), но опять же, это предполагает, что на веб-хостинге доступен SSIS (это не дано). И я не верю, что SSIS имеет возможность удалять смешные символы — не так ли? Итак, вы пропустили эту лодку, покидающую гавань по двум причинам.

Ответ №1:

Проблема в том, что вы читаете все строки одновременно, что, очевидно, вызовет такие проблемы, как OutOfMemoryException .

Что вы хотели бы сделать, ТАК это НЕ загружать все данные из OleDbDataReader DataTable объекта сразу.

Вот мое предлагаемое решение, как я вижу, вы хотите массово вставлять строки:

  • После вызова ExecuteReader() метода получите схему
    таблицы.
  • Получите текущую схему таблицы и добавьте столбцы в свой DataTable объект. (Этот DataTable объект будет использоваться для массовой публикации строк в SQL)
  • Прочитайте OleDbDataReader строку за строкой и скопируйте эти строки в то DataTable место, куда вы скопировали схему. ( dr.Read() метод будет считывать данные строка за строкой).
  • Проверьте, когда количество строк превышает 1000 или если больше строк нет, отправьте те записи, которые у вас есть, в свой SQL, используя BulkCopy затем очистите таблицу. (Вы можете увеличить это число в зависимости от состояния вашей памяти)
  • Повторяйте, пока не останется больше строк.

Вот ваш код, переписанный предложенным мной способом:

 using (System.Data.OleDb.OleDbDataReader dr = cmd.ExecuteReader())
{
    var schemaTable = dr.GetSchemaTable(); // Get Metadata of the current table.
    var dt = new DataTable();
    foreach (DataRow row in schemaTable.Rows) // Copy the schema to your datatable object
    {
        string colName = row.Field<string>("ColumnName");
        System.Type t = row.Field<System.Type>("DataType");
        dt.Columns.Add(colName, t);
    }
    bool hasNextRow = false;
    do
    {
        if (!hasNextRow) 
        {
            // We have to do this in order to peek through the next row. If we do not have next row, then we will have to commit the current changes
            hasNextRow = dr.Read();
        }
        if(!hasNextRow) break; // Break if there is no row.
        var newRow = dt.NewRow(); 
        foreach (DataColumn col in dt.Columns) // Copy current row
        {
            newRow[col.ColumnName] = dr[col.ColumnName];
        }
        dt.Rows.Add(newRow);
        dt.AcceptChanges();
        hasNextRow = dr.Read();
        if (dt.Rows.Count >= 1000 || !hasNextRow) // When the data exceeds thousands rows Or when there are no further rows, insert the data into sql and clear the memory. 
        {
            foreach (DataRow row in dt.Rows) // Adjust the values
            {
                 for (int i = 0; i < dt.Columns.Count; i  )
                 {
                     if (dt.Columns[i].DataType == typeof(string))
                         row[i] = Regex.Replace(row[i].ToString(), "[#$%^*@!~?]", "");
                 }
            }
            dt.AcceptChanges();
            // Bulk Copy to SQL Server
            using (SqlBulkCopy bulkCopy = new SqlBulkCopy(conn))
            {
                // code for bulk insert
                bulkCopy.WriteToServer(dt);
            }

            dt.Rows.Clear(); // Clear the memory so new rows can be read.
            dt.AcceptChanges();
        }
    } while(hasNextRow);
}
  

PS: Код не тестировался, поэтому, возможно, потребуется немного любви.