#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: Код не тестировался, поэтому, возможно, потребуется немного любви.