#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 записей.