Самый быстрый способ загрузить данные из текстового файла, а затем сохранить их в базе данных

У меня проблемы.

Я разрабатываю проект, но я застрял в этой части:

Я хочу загрузить данные из текстового файла и сохранить их в доступе к базе данных. Это данные внутри каждого текстового файла, около 12 000 строк данных, и для обработки каждого текстового файла требуется около 10 минут.

ПРИМЕЧАНИЕ. Перед сохранением данных я отделяю каждую строку данных от текстового файла и помещаю ее в строку, затем проверяю, находятся ли данные уже в базе данных или нет. если внутри базы данных я обновляю ее. Если нет, то я использую оператор вставки.

Я использую C# для разработки этой программы? есть ли самый быстрый способ загрузить и сохранить эти данные?

ОБНОВЛЕНО:

Это мой код, надеюсь, он поможет понять мои проблемы:

    using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Collections;
using System.Data.OleDb;

namespace DAF
{
    public partial class FrontForm : Form
    {
        public Boolean status;

        public FrontForm()
        {
            InitializeComponent();

            //define location of the database
            string connection = @"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\Users\PC\Desktop\Graduation Project\Research\DAF\Data\DAFLogisticDepartment.mdb";

            //define location of the text file data
            DirectoryInfo di = new DirectoryInfo(@"C:\Users\PC\Desktop\Graduation Project\Research\DAF\Data\");
            FileInfo[] fiarr = di.GetFiles("*.txt");


            //define connection to database
            OleDbConnection con = new OleDbConnection(connection);
            String query;
            OleDbDataReader rdr = null;

            con.Open();
            //get all table from database
            OleDbCommand cmd = con.CreateCommand();
            DataTable dt = con.GetSchema("tables");
            DataRow[] dttable = dt.Select();
            con.Close();

            //read each new textfile inside the folder
            foreach (FileInfo fri in fiarr)
            {
                StreamReader sr = new StreamReader(fri.FullName, System.Text.Encoding.Default);
                String line;
                String tabledbs, dbsName;

                while ((line = sr.ReadLine()) != null)
                {
                    String VRSD, locationID, truckID, yearIn, yearOut, weekIn, weekOut, dayIn, dayOut, timeIn, timeOut, route;
                    int plantID;

                    //process each line of data and put into each variable
                    VRSD = line.Substring(0, 4).Trim();
                    plantID = Convert.ToInt32(line.Substring(4, 1).Trim());
                    locationID = line.Substring(5, 4).Trim();
                    truckID = line.Substring(24, 5).Trim();
                    yearIn = line.Substring(32, 4).Trim();
                    weekIn = line.Substring(36, 2).Trim();
                    dayIn = line.Substring(38, 1).Trim();
                    timeIn = line.Substring(39, 8).Trim();
                    yearOut = line.Substring(47, 4).Trim();
                    weekOut = line.Substring(51, 2).Trim();
                    dayOut = line.Substring(53, 1).Trim();
                    timeOut = line.Substring(54, 8).Trim();
                    route = line.Substring(64, 2).Trim();

                    //make database name
                    dbsName = plantID + locationID;

                    con.Open();
                    //check if the table exist in database
                    for (int i = 0; i < dttable.Length - 9; i++)
                    {
                        tabledbs = dttable[i]["TABLE_NAME"].ToString();
                        ArrayList indexlist = new ArrayList();

                        if (tabledbs == dbsName)
                        {
                            //if the table exist, status = true
                            status = true;
                            break;
                        }
                    }
                    con.Close();

                    con.Open();

                    if (status == true)
                    {

                        try
                        {        
                            //if the data not in the system, insert statement
                            query = @"insert into " + plantID + locationID + " values('" + VRSD.ToString() + "'," + plantID + ",'" + locationID + "','" + truckID + "','" + yearIn + "','" + weekIn + "','" + dayIn + "','" + timeIn + "','" + yearOut + "','" + weekOut + "','" + dayOut + "','" + timeOut + "')";
                            cmd = new OleDbCommand(query, con);
                            rdr = cmd.ExecuteReader();
                            con.Close();
                        }
                        catch
                        {
                            //if the data in the system, update statement
                            query = @"update " + dbsName + " set YearIn='" + yearIn + "', YearOut='" + yearOut + "', WeekIn='" + weekIn + "', WeekOut='" + weekOut + "', DayIn='" + dayIn + "', DayOut='" + dayOut + "', TimeIn='" + timeIn + "', TimeOut='" + timeOut + "' where LocationID='" + locationID + "' and PlantID=" + plantID + "";
                            cmd = new OleDbCommand(query, con);
                            rdr = cmd.ExecuteReader();
                            con.Close();
                        }

                    }
                    else
                    {
                        //create new table
                        string attribute = "VRSD String,PlantID Integer, LocationID String, TruckID String," +
                                            "YearIn String, WeekIn String, DayIn String, TimeIn String," +
                                            "YearOut String, WeekOut String, DayOut String, TimeOut String";

                        query = @"CREATE TABLE " + plantID + locationID + "(" + attribute + ")";
                        cmd = new OleDbCommand(query, con);
                        cmd.ExecuteNonQuery();

                        //insert the data
                        query = @"insert into " + plantID + locationID + " values('" + VRSD.ToString() + "'," + plantID + ",'" + locationID + "','" + truckID + "','" + yearIn + "','" + weekIn + "','" + dayIn + "','" + timeIn + "','" + yearOut + "','" + weekOut + "','" + dayOut + "','" + timeOut + "')";
                        cmd = new OleDbCommand(query, con);
                        rdr = cmd.ExecuteReader();
                        con.Close();
                    }

                    status = false;
                }
                sr.Close();

                //after the text file load into database, the text file moved to history folder
                MessageBox.Show(fri.FullName.ToString(), "File Manager", MessageBoxButtons.OK);
                fri.MoveTo(@"C:\Users\PC\Desktop\Graduation Project\Research\DAF\Data\History\" + fri.Name.ToString() + ".txt");
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            StandardReport sr = new StandardReport();
            sr.Show();
        }

        private void FrontForm_Load(object sender, EventArgs e)
        {

        }
    }
}

person Yohanes    schedule 11.02.2011    source источник
comment
10 минут это так плохо? Сколько файлов и как часто вам нужно это делать? Можете ли вы распараллелить задачи, которые вы делаете? Вы профилировали это, чтобы увидеть, на что уходит время?   -  person btlog    schedule 11.02.2011
comment
вот в чем дело: мой босс хочет загружать 6 текстовых файлов каждую неделю.. и каждый текстовый файл содержит более 12 000 строк данных.. каждый раз, когда мой босс запускает программу, программа загружает каждый текстовый файл, проверяет каждую строку в базе данных данные есть или нет. Если он существует, он обновляет его, если нет, вставляет в базу данных... и каждый текстовый файл занимает около 10 минут. Если есть 6 текстовых файлов, то это занимает 1 час.   -  person Yohanes    schedule 11.02.2011
comment
Я добавляю свой код. Надеюсь, вы мне поможете.. :)   -  person Yohanes    schedule 11.02.2011
comment
@Yohanes Еще один вопрос, какой формат текстовых файлов, которые вы хотите вставить? Небольшая выборка данных поможет.   -  person Fionnuala    schedule 11.02.2011
comment
У вас есть 12 тыс. строк, x6... Вы ожидаете, что большинство из них будут новыми или существующими строками?   -  person RichardW1001    schedule 11.02.2011
comment
Я заметил, что вы создаете новую таблицу, если идентификатор растения + идентификатор местоположения не существует, что кажется мне довольно странным дизайном для базы данных.   -  person Fionnuala    schedule 11.02.2011
comment
to Remou: ну, мой босс хочет автоматически создать новую таблицу, если данные содержат новую таблицу.. я думал об использовании VBA вместо C#, но после того, как я загрузил эти данные в базу данных, мне все равно нужно обработать их в отчете в excel.. вот почему Я использую C # в качестве моста для этих двух программ. Это пример 1-строчных данных: VRSD3AAS ACHTERAS LIJN L09561A*201105521011900201106106282800A 50.. надеюсь, вы поможете мне найти способ сделать это быстро :) спасибо   -  person Yohanes    schedule 13.02.2011


Ответы (4)


Большим убийцей времени здесь будет огромное количество подключений к базе данных, которые вы используете — попробуйте создать список команд в памяти (что почти не займет времени для каждого объекта по сравнению с чтением данных из файла), и как только вы создал свой список, выполняйте их все через одно соединение. Для открытия каждого соединения требуется время, и вы делаете это гораздо чаще, чем нужно. Редактировать - на самом деле заметил, что вы открываете/закрываете 2 соединения на строку в файле!


В настоящее время (псевдокод для ясности):

For each file (x6)

   Load file from stream

   For each line in file (x12k)

     Read data from line

     Open database connection (happens 72k times)
     Check whether table exists
     Close connection

     Open connection (x72k)
     Try to insert record
     If inserting fails, update existing record
     Close connection

     Next line

   Close filestream

Next file

Предложение: (и настоятельно рекомендуем вам подумать о последствиях динамического добавления таблиц, обычно это не очень хорошее решение, но если оно вам навязывается, возможно, у вас нет выбора)

Create an in-memory list of commands 
  (or list of custom objects with property for each command type, create 
  table,insert,update)    

For each file (x6)

   Load file from stream

   For each line in file (x12k)

      Read data from line (all happens 72k times, but no external connections per line)

      Write your create table command
      Write your insert command
      Write your update command
      Add to relevent command lists

   Next Line

   Close filestream

Next File

Open database connection (x1)

For each command in your list
   Apply suitable logic as to whether command needs to execute
   Execute command if applicable
Next command

Close database connection
person RichardW1001    schedule 11.02.2011
comment
Итак, вы предлагаете, чтобы все данные, которые я сохранил, были временными, а затем объединены в базу данных за один раз? я все еще не понимаю, как поместить / вставить все данные в базу данных в одном соединении ... можете ли вы привести мне пример? Благодарность!! :) - person Yohanes; 13.02.2011
comment
добавил грубый пример, чтобы продемонстрировать - person RichardW1001; 14.02.2011

Почему бы вам не попробовать создать и использовать пакет SSIS? Он очень хорош в такого рода вещах, имеет отличные инструменты и довольно прост в использовании из кода.

http://msdn.microsoft.com/en-us/library/ms141026.aspx

http://blogs.msdn.com/b/michen/archive/2007/03/22/running-ssis-package-programmatically.aspx

person Veli Gebrev    schedule 11.02.2011
comment
Поможет ли это с текстом и MS Access? Установка SQL Server кажется излишней для этой задачи. - person Fionnuala; 11.02.2011
comment
Я не знаю, если вы работаете с таблицами с более чем 12 тысячами записей, не может пройти так много времени, прежде чем вы перерастете MS Access. Стоит посмотреть серьезно, лучше иметь запас по высоте? - person RichardW1001; 11.02.2011
comment
@Remou, да, SSIS может работать с несколькими типами поставщиков источников и получателей ваших данных, включая плоские файлы (текст) и JET/ACE (доступ). - person Veli Gebrev; 12.02.2011
comment
Я знаю, что SSIS будет работать со многими вещами, но это очень большой молот для крошечной ошибки. - person Fionnuala; 12.02.2011
comment
Обратите внимание, что каталог, содержащий данные, содержится в папке под названием Graduation Project, которая в сочетании со столбцом (полем) PlantID и тем, что выглядит как очень ненормальная структура базы данных, говорит о многом. Например, что все это можно сделать на VBA без каких-либо проблем и что SQL Server, вероятно, потребует приложения для ИТ-отдела колледжа, на обработку которого, в зависимости от колледжа, могут уйти недели. - person Fionnuala; 12.02.2011

Вы можете использовать запрос для вставки несовпадающих записей с помощью драйвера Jet для текста.

SELECT a.* INTO NewTable FROM 
(SELECT * From [Text;DSN=Import Link Specification;FMT=Delimited;HDR=NO;IMEX=2;CharacterSet=850;DATABASE=C:\Docs].[Import.txt]) As A
LEFT JOIN OldTable ON a.Key=OldTable.Key
WHERE a.Key Is Null

РЕДАКТИРОВАТЬ

Интересно, почему у вас нет основной таблицы, содержащей все растения и локации. Затем вы можете вставить все файлы во временную таблицу и либо добавить, либо обновить временную таблицу соответственно.

foreach (FileInfo fri in fiarr)
    {
    string s = "[Text;DSN=Test Spec;"
         + "FMT=Fixed;HDR=Yes;IMEX=2;CharacterSet=850;DATABASE=" 
         + fri.DirectoryName + "].["
         + fri.Name + "]";

    query = "INSERT INTO Temp SELECT * FROM " + s;

    cmd.ExecuteNonQuery();
    }

Похоже, вы используете формат фиксированной длины, поэтому DSN=Test Spec — это спецификация Access, созданная путем экспорта файла в формате фиксированной ширины и последующего сохранения спецификации с помощью кнопки «Дополнительно».

person Fionnuala    schedule 11.02.2011
comment
Полагаю, что так. Общая идея заключается в том, что вы просто выполняете запрос к соединению. Я посмотрю, смогу ли я что-то сделать с вашим кодом. - person Fionnuala; 11.02.2011
comment
Кстати, видя, что это Access, вам даже не нужен C #, это можно очень просто запустить в VBA как кучу запросов. - person Fionnuala; 11.02.2011

Одной из проблем здесь может быть то, что вы вставляете каждую запись построчно с помощью SINGLE SQL EXECUTE.

Другим решением будет:

  1. Считайте текстовый файл в строковый буфер (20 000 строк).
  2. Создайте объект DataTable
  3. В цикле вставьте каждую строку в объект DataTable.
  4. Наконец, с помощью DataAdapter запишите DataTable обратно в базу данных.
person Community    schedule 09.05.2012