Есть ли более быстрый способ чтения из файла и вставки в SQLite? - PullRequest
0 голосов
/ 31 августа 2010

Полдень всем.Я не очень знаком с SQLite, поэтому я не испортил все настройки базы данных.Я довольно знаком с SQL Server, Oracle и даже некоторыми Access и mySQL.Ну, в настоящее время я беру файл с более чем 110 000 записей и построчно читаю файл, анализирую данные и выполняю оператор вставки в таблицу.Таблица зависит от типа записи, которая является первым полем строки.Ну, я загружаю его прямо сейчас, и он работает в течение 12 минут (как я пишу это) и импортировал только 14 000 записей.Выполнение математики означает, что это займет от 1 часа 15 минут до 1 часа 30 минут.В зависимости от того, как действует остальная часть моей системы.Поскольку существуют разные типы записей, я не мог выполнить массовую вставку, если была опция для SQLite (не уверен, есть ли).Это работает как фоновый работник.Ниже приведена функция, которая извлекает и анализирует данные, а также функция, которая вставляет их в базу данных.Имейте в виду, что это приложение C # в формате MVC (было так, когда я взял его под контроль и не успел его реструктурировать):

MainForm.cs Фоновые рабочие функции

#region Background Worker Functions

    #region private void InitializeBackgroundWorker()
    /*************************************************************************************
    *************************************************************************************/
    private void InitializeBackgroundWorker()
    {
        backgroundWorker.DoWork +=
            new DoWorkEventHandler(backgroundWorker1_DoWork);
        backgroundWorker.RunWorkerCompleted +=
            new RunWorkerCompletedEventHandler(
        backgroundWorker1_RunWorkerCompleted);
        backgroundWorker.ProgressChanged +=
            new ProgressChangedEventHandler(
        backgroundWorker1_ProgressChanged);
    }
    #endregion

/*****************************************************************************************************************************************************************************************************/

    #region private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    /*************************************************************************************
    *************************************************************************************/
    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        // Get the BackgroundWorker that raised this event.
        BackgroundWorker worker = sender as BackgroundWorker;

        // Assign the result of the computation
        // to the Result property of the DoWorkEventArgs
        // object. This is will be available to the 
        // RunWorkerCompleted eventhandler.

        //Creates a static singleton file list.  Remains on the stack and can be accessed anywhere without
        // reinstatiating
        object[] obj = (object[])e.Argument;
        string fileName = obj[0].ToString();
        DataController controller = new DataController(worker, e);
        controller.FileName = fileName;
        try
        {
            if (strProcess == "Import")
            {
                controller.Import();
            }
            else if (strProcess == "Export")
            {
                controller.ExportToExcel();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message.ToString());
        }
    }
    #endregion

/*****************************************************************************************************************************************************************************************************/

    #region private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    /*************************************************************************************
    *************************************************************************************/
    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error != null)
        {
            MessageBox.Show(e.Error.StackTrace);
        }
        else
        {
            this.toolStripStatusLabel1.Text = "Import complete";
            generateReport();
            treeViewFigure.Nodes.Clear();
            BuildTree();
            treeViewFigure.TopNode.ExpandAll();
            labelIPBNumber.Text = controller.IPBNumber;
            this.Text += "IPB: " + labelIPBNumber.Text;

            cmbIndentureLevel.Items.Clear();
        }
    }
    #endregion

/*****************************************************************************************************************************************************************************************************/

    #region private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    /*************************************************************************************
    *************************************************************************************/
    private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
    {
        string stat = e.UserState.ToString();
        this.toolStripStatusLabel1.Text = "";
        this.toolStripStatusLabel1.Text = stat;
        this.toolStripProgressBar1.Value = e.ProgressPercentage;
    }
    #endregion

#endregion

Importer.cs Функция импорта

    #region public void Import(string fileName)
    /*************************************************************************************
    *************************************************************************************/
    public void Import(string fileName)
    {
        if (!File.Exists(fileName))
        {
            throw new FileNotFoundException();
        }

        StreamReader read = File.OpenText(fileName);
        List<RecordBase> List = new List<RecordBase>();
        DataFactory factory = DataFactory.BuildFactory();

        int nLines = 0;

        while (!read.EndOfStream)
        {
            read.ReadLine();
            nLines++;
        }

        read.Close();
        read = File.OpenText(fileName);

        factory.lstObservers = _observers;
        factory.ClearDB();

        int count = 1;

        while (!read.EndOfStream)
        {
            string[] fields = read.ReadLine().Split('|');
            List<string> lstStr = new List<string>();
            foreach (string str in fields)
            {
                lstStr.Add(str);
            }

            lstStr.RemoveAt(fields.Length - 1);
            fields = lstStr.ToArray();

            string strValues = string.Join("','", fields);
            strValues = "'" + strValues + "'";
            if (fields.Length >= 39 && fields[0] == "03")
            {
                factory.ImportTaggedRecord(fields[38], count);
                int nIndex = strValues.IndexOf(fields[38]);
                strValues = strValues.Substring(0, nIndex - 2);
            }

            factory.ImportIPB(strValues, fields[0], count);

            progress.ProgressComplete = (count * 100) / nLines;
            progress.Message = "Importing Record: " + count++.ToString();
            Notify();
        }
    }
    #endregion

DataFactory.cs Функция ImportIPB

    #region public void ImportIPB(string strValues, string strType)
    /*************************************************************************************
    *************************************************************************************/
    public void ImportIPB(string strValues, string strType, int nPosition)
    {
        string strCommand = string.Empty;

        switch (strType)
        {
            case "01":
                strCommand = Queries.strIPBInsert;
                break;
            case "02":
                strCommand = Queries.strFigureInsert;
                break;
            case "03":
                strCommand = Queries.strPartInsert;
                break;
        }

        ExecuteNonQuery(strCommand + strValues + ", " + nPosition.ToString() + ")");
    }
    #endregion

Database.cs Метод ExecuteNonQuery

    #region public void ExecuteNonQuery(string strSQL)
    /*************************************************************************************
    *************************************************************************************/
    public void ExecuteNonQuery(string strSQL)
    {
        DbCommand dbCommand = _dbConnection.CreateCommand();
        dbCommand.CommandText = strSQL;
        dbCommand.Prepare();
        dbCommand.ExecuteNonQuery();
    }
    #endregion

Может ли кто-нибудь что-нибудь увидетьиз того, что может быть улучшено?Есть ли настройки для фонового рабочего, которые можно настроить для более быстрой работы?Есть ли настройки по умолчанию для фонового рабочего?Какие настройки имеются в файле базы данных, которые можно изменить (используя SQLite Expert Personal), чтобы ускорить вставку?Это просто размер моего файла?Прямо сейчас, когда я заканчиваю это, прошло всего 22 минуты и было сделано 24 000 записей.Это не чувствителен ко времени, поэтому занимайте все необходимое время.Спасибо.

Обновление : Кроме того, я должен упомянуть, что в одной из таблиц у меня есть целочисленный первичный ключ (действует как поле идентификатора).Может ли это иметь проблемы с производительностью?

Ответы [ 4 ]

5 голосов
/ 31 августа 2010

Используйте один SQLiteTransaction вокруг всей вставки. Таким образом, он будет принудительно сбрасываться в файл после каждой вставки, чтобы сохранить соответствие ACID. Как и с любыми DbConnection и DbTransaction, вы используете BeginTransaction, затем, когда вы закончите, Commit. Вся вставка будет успешной или неудачной, и будет иметь лучшую производительность.

1 голос
/ 31 августа 2010

FWIW, клиент командной строки для SQLite имеет встроенную команду загрузки данных.Но если вы прочитаете код C этого клиента SQLite, вы увидите, что он не делает ничего особенного.Он просто читает строку вашего файла данных построчно и выполняет INSERT в цикле.

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

Вы также можете отключить журнал отката :

PRAGMA journal_mode = OFF

или установить запись на асинхронный , позволяющий операционной системе буферизовать ввод / вывод:

PRAGMA synchronous = OFF

Эти изменения в прагме должны сэкономить значительные накладные расходы ввода / вывода.Но без журнала отката команда ROLLBACK не будет работать, и, если во время текущей транзакции произойдет сбой приложения, ваша база данных может быть повреждена.Без синхронной записи сбой операционной системы также может привести к потере данных.

Не пытаясь вас напугать, но вы должны знать, что существует компромисс между производительностью и гарантированной целостностью ввода / вывода.Я рекомендую работать с включенными безопасными режимами большую часть времени, и отключать их ненадолго, когда вам нужно выполнить большую загрузку данных, как вы делаете - тогда не забудьте снова включить безопасные режимы!

1 голос
/ 31 августа 2010

Первое, что повысит производительность вставки, - это начать только одну транзакцию. Это приведет к ускорению ваших вставок на порядок.

См. здесь для записи FAQ, описывающей это явление.

0 голосов
/ 05 сентября 2012

Я экспериментировал и нашел, что самый быстрый способ импортировать большую базу данных в SQLite с помощью C # - на самом деле создать дамп в CSV, а затем использовать инструмент командной строки sqlite3.exe

С большим файлом, содержащим около25 миллионов строк для вставки на моем ноутбуке

Оптимизированная вставка с помощью оболочки .NET: 30 минут (оптимизировано для транзакций, параметризованных команд, отключения журнала и т. Д.)

Дамп в CSV (2 минуты), затемимпорт CSV с помощью sqllite3.exe (5 минут): 7 минут

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...