Проблема с производительностью базы данных SQLite с VB.NET

#vb.net #sqlite

#vb.net #sqlite

Вопрос:

Я вставляю таблицу данных в базу данных SQLite. Я делаю вот так. Сначала я извлекаю данные с помощью функции getdata и вставляю их в datatable, затем с помощью For Each Loop я создал команду Insert и выполнил ее. У меня 50000 записей, для запуска потребуется 30 минут. Пожалуйста, укажите подходящий подход. Вот код.

 Dim xtable As DataTable = getdata("select * from tablename")
Dim str As String = Nothing
For Each r As DataRow In xtable.Rows  ''''HERE IT WILL TAKE TOO MUCH TIME 
     str = str amp; ("insert into tablename values(" amp; r.Item("srno") amp; "," amp; r.Item("name"));")
Next
EXECUTEcmd(str)   

Public Function getdata(ByVal Query As String) As DataTable
    connectionString()
    Try
        Dim mds As New DataTable
        Dim mycommand As New SQLiteCommand(DBConn)
        mycommand.CommandText = Query
        Dim reader As SQLiteDataReader = mycommand.ExecuteReader()
        mds.Load(reader)
        Return mds
    Catch ex As Exception
        MsgBox("DB Error", vbCritical, "")
        MsgBox(Err.Description)
        Return Nothing
    End Try
End Function

 Public Sub EXECUTEcmd(ByVal selectcmd As String)
    Using cn = New SQLiteConnection(conectionString)
        cn.Open()
        Using transaction = cn.BeginTransaction()
            Using cmd = cn.CreateCommand()
                cmd.CommandText = selectcmd
                cmd.ExecuteNonQuery()
            End Using
            transaction.Commit()
        End Using
        cn.Close()
    End Using
End Sub
  

здесь строка подключения:

 conStr = "Data Source=" amp; dbpath amp; ";Version=3;Compress=True; UTF8Encoding=True; PRAGMA journal_mode=WAL; cache=shared;"

  
  

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

1. Это одна и та же таблица в операторах SELECT и INSERT?

2. Нет, обе таблицы разные…. Получение данных из одной таблицы и вставка их в другую

3. одновременный импорт данных с SQLServer в SQLite, содержащий 50000 записей. Есть ли лучшее решение для выполнения той же задачи.

Ответ №1:

Используйте stringbuilder для построения вашей строки, а не конкатенации строк

 
Dim strB As StringBuilder = New StringBuilder(100 * 50000)
For Each r As DataRow In xtable.Rows  
     strB.AppendLine($"insert into tablename values({r.Item("srno")},{r.Item("name")});")
Next
  

Строки не могут быть изменены в .net. Каждый раз, когда вы создаете новую строку, VB должен скопировать все из старой строки в новую и добавить новый бит, который вы хотите. Если каждая из ваших инструкций insert составляет 100 байт, это означает, что она копирует 100 байт, затем добавляет 100, затем копирует 200 байт и добавляет 100, затем копирует 300 байт, затем 400 байт, затем 500 байт. К тому времени, когда он обработал 10 строк, он скопировал 5,5 килобайт. К тому времени, когда он закончил 50 тысяч строк, он скопировал 125 гигабайт данных. Неудивительно, что это медленно!

Всегда используйте StringBuilder для построения массивных строк

Я готов игнорировать проблему взлома sql-инъекций из-за характера задачи, но, пожалуйста, прочитайтеhttp://bobby-tables.com — вы никогда не должны объединять значения в SQL как способ создания sql, который имеет некоторый изменяющийся эффект.

Все это упражнение было бы лучше выполнить как нечто подобное (псевдокод):

 Dim sel as New SQLiteCommand("SELECT a, b FROM table", conn)
Dim ins as New SQLiteCommand("INSERT INTO table VALUES(:a, :b)", conn)
ins.Parameters.Add("a" ...)
ins.Parameters.Add("b" ...)

Dim r = sel.ExecuteReader()

While r.Read()
  ins.Parameters("a") = r.GetString(0)
  ins.Parameters("b") = r.GetString(1)
  ins.ExecuteNonQuery()
End While
  

То есть вы минимизируете объем памяти, считывая строки по одной за раз из хранилища и вставляя их по одной за раз во вставку; команда insert готовится один раз, вы просто меняете значения параметров, выполняете ее, меняете их снова, выполняете ее … Это то, для чего были разработаны параметризованные запросы (а также для предотвращения взлома вашего приложения, когда кто-то вводит SQL в вашу переменную, или даже просто останавливает его сбой, когда у вас есть человек по имени О’Грейди

Ответ №2:

Возможно, вам необходимо провести рефакторинг вашего кода следующим образом:

 Dim xtable As DataTable = getdata("select * from tablename")

Using cn = New SQLiteConnection(conectionString)
    cn.Open()
    Using transaction = cn.BeginTransaction()
        Try
            Using cmd = cn.CreateCommand()
                cmd.Transaction = transaction
                For Each r As DataRow In xtable.Rows  ''''HERE IT WILL TAKE TOO MUCH TIME 
                    cmd.CommandText = "insert into tablename values(" amp; r.Item("srno") amp; "," amp; r.Item("name") amp; ")"
                    cmd.ExecuteNonQuery()
                Next
            End Using
            transaction.Commit()
        Catch ex As Exception
            transaction.Rollback()
        End Try
    End Using
End Using

Public Function getdata(ByVal Query As String) As DataTable
    connectionString()
    Try
        Dim mds As New DataTable
        Dim mycommand As New SQLiteCommand(DBConn)
        mycommand.CommandText = Query
        Dim reader As SQLiteDataReader = mycommand.ExecuteReader()
        mds.Load(reader)
        Return mds
    Catch ex As Exception
        MsgBox("DB Error", vbCritical, "")
        MsgBox(Err.Description)
        Return Nothing
    End Try
End Function
  

Вместо объединения возможной гигантской строки, оберните все ваши вставки в одну транзакцию, как указано выше. Это уменьшит объем используемой памяти, а также ускорит работу sqlite.

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

1. приведенный выше код работает, но когда я делаю это без цикла for. она остается очень медленной. Используя cmd = cn.createCommand() cmd. Транзакция = transaction cmd. CommandText = sqlstring cmd. Завершите использование ExecuteNonQuery(). Эта строка sqlstring, имеющая 50000 записей.