Что нового

Функция для отправки данных в базу SQLite - обратный аналог _SQLite_FetchData

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Medic84 верю, не заглядывал туда. А вы хорошо заморочились с функцией, молодец!
 
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
inververs
inververs сказал(а):
veretragna собрались изучать C++, а биндинг параметров не осилили?

Вы правы, я на С++ только балаболить умею. За плечами пара совсем мелких простейших проектов, и я все еще плохо понимаю, что там происходит, но я стараюсь. И рано или поздно таки научусь писать что-то полезное! :laugh:

Medic84, огромное спасибо!
Постараюсь использовать Ваш пример и сделать универсальную функцию для массовой привязки и отправки данных, позже отпишусь в этой же теме.
Будет сравнение "мегабайтных строк" с этим способом, с бл преферансом и таймерами.
 
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
Подпилил немного функции по массовой вставке значений, готов поделиться результатами.

Условия: Массив на 2000 строк по 4 элемента: String (12 знаков), Int, Double, BLOB (3000 байт).
Код:
_SQLite_Exec(-1, "CREATE TABLE 'TestTable' (Datestamp string, Size_O int, Divisor double, Data blob);")


Результаты:
* в среднем 1390 мс при выполнении в цикле:
Код:
_SQLite_Exec(-1, "INSERT INTO 'TestTable' VALUES ('" & $Datestamp & "', " & $Size_O & ", " & $Divisor & ", " & $Data & ");")


* в среднем 1241 мс при выполнении в цикле кода массовой вставки значений:
Код:
_SQLite_BindText($hQuery, 1, $Datestamp)
_SQLite_BindInt($hQuery, 2, $Size_O)
_SQLite_BindDouble($hQuery, 3, $Divisor)
_SQLite_BindBlob($hQuery, 4, $Data)
_SQLite_Step($hQuery)
_SQLite_ClearBindings($hQuery)
_SQLite_QueryReset($hQuery)


Налетай, торопись, пробуем!
Если кому интересно, библиотечка выложена отдельным постом в разделе UDF.
 

Naisho

Знающий
Сообщения
86
Репутация
12
Так быстрее :smile:
Код:
#include <SQLite.au3>
#include <SQLite.batch.au3>

If Not FileExists(@ScriptDir & "\sqlite3.dll") then FileInstall("sqlite3.dll", @ScriptDir & "\sqlite3.dll")

_SQLite_Startup(@ScriptDir & "\sqlite3.dll", Default, 1)
Global $hDB = _SQLite_Open(@ScriptDir & '\ex.sqlite')

$TableName = 'TestTable'

_SQLite_Exec(-1, "CREATE TABLE IF NOT EXISTS '" & $TableName & "' (Datestamp string, Counter1 int, Counter2 double, Data blob);")

Local $Data[100000][5]
For $i = 0 to 99999
	$Data[$i][0] = "teststring"
	$Data[$i][1] = $i*2
	$Data[$i][2] = $i/3
	$Data[$i][3] = Binary("This is string in BLOB with 3 trailing zeroes." & Chr(0) & Chr(0) & Chr(0))
Next


$t = TimerInit()
_SQLite_Exec(-1, "BEGIN TRANSACTION;")
_SQLite_BatchInsert(-1, $TableName, $Data, "Text|Int|Double|Blob")
_SQLite_Exec(-1, "COMMIT;")
$dt1 = TimerDiff($t)

_SQLite_Exec(-1, "DROP TABLE IF EXISTS '" & $TableName & "';")

_SQLite_Exec(-1, "CREATE TABLE IF NOT EXISTS '" & $TableName & "' (Datestamp string, Counter1 int, Counter2 double, Data blob);")

$t = TimerInit()
_FillTableEX()
$dt2 = TimerDiff($t)

MsgBox(64,"",$dt1 & @crlf & $dt2 & @crlf & $dt1/$dt2)



_SQLite_Close()
_SQLite_Shutdown()

Func _FillTableEX()
	Local $sSqlBuild_Query = "", $k = 0, $iCount = UBound($Data), $n = Int($iCount/100)
	For $i = 0 To $iCount-1 Step $n
		$k = (($i+$n) > $iCount) ? ($iCount-$i-2) : ($n-2)
		$sSqlBuild_Query &= "INSERT INTO '" & $TableName & "' (Datestamp, Counter1, Counter2, Data) VALUES "
		For $j = 0 To $k
			$sSqlBuild_Query &= "('" & StringReplace( $Data[$i+$j][0], "'", "''") & "'"
			$sSqlBuild_Query &= "," & $Data[$i+$j][1]
			$sSqlBuild_Query &= "," & $Data[$i+$j][2]
			$sSqlBuild_Query &= ",'" & $Data[$i+$j][3] & "'),"
		Next
		$sSqlBuild_Query &= "('" & StringReplace( $Data[$i+$j][0], "'", "''") & "'"
		$sSqlBuild_Query &= "," & $Data[$i+$k+1][1]
		$sSqlBuild_Query &= "," & $Data[$i+$k+1][2]
		$sSqlBuild_Query &= ",'" & $Data[$i+$k+1][3] & "');"
	Next
	_SQLite_Exec(-1, $sSqlBuild_Query )
EndFunc
 
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
Если эти фрагменты кода поменять местами, то Ваш алгоритм медленнее.
А если провести хотя бы 10 тестов с разными данными и посчитать среднее время, мой алгоритм выигрывает с большим отрывом.
 

Medic84

Омега
Команда форума
Администратор
Сообщения
1,590
Репутация
341
Ну кто устраивает тесты на маленькой выборке?) Сделайте миллион запросов, и тестируйте.
 

Medic84

Омега
Команда форума
Администратор
Сообщения
1,590
Репутация
341
Naisho [?]

Да, но Вы не первый кто предложил подобное. ТС это не подходит - так как 1 сбой может убить много информации. Ведь все находится в одной строке.

veretragna [?]
Вы правы, вызывается только 1 раз.Но речь шла о десятках тысяч запросов за раз, а то и не десятках - кто знает, сколько данных накопится, например, через год работы программы.В Вашем примере можно легко упереться в технический лимит длины одной строки запроса.А сколько времени движок SQLite будет его обрабатывать - вообще открытый вопрос, это же могут быть мегабайты только одних запросов.Намного эффективнее и изящнее все-таки создать один экземпляр query, привязывать к нему данные и отправлять в базу.При этом, если данные находятся в массиве, мы избегаем просадок скорости, которые наблюдаются при сшивании нескольких строк в одну. Особенно, опять же, если строчек - десятки тысяч.
 
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
Medic84, набор в 2000 значений выбран неспроста. Дело в том, что последний аргумент запроса (blob) внутри моего проекта предварительно проходит процедуру сжатия прямо в оперативной памяти. Долго рассказывать, просто скажу, что код выдран с рабочего проекта, и если дать скрипту миллион запросов, работать будет очень долго.
Вы правы, нужно выбрать набор данных побольше. О результатах отпишусь чуть позже.
 

Naisho

Знающий
Сообщения
86
Репутация
12
поставил исправление - всё равно на 100 тысячах строк бинд медленнее примерно на 420%

Код:
#include <SQLite.au3>
#include <SQLite.batch.au3>

If Not FileExists(@ScriptDir & "\sqlite3.dll") then FileInstall("sqlite3.dll", @ScriptDir & "\sqlite3.dll")

_SQLite_Startup(@ScriptDir & "\sqlite3.dll", Default, 1)
Global $hDB = _SQLite_Open(@ScriptDir & '\ex.sqlite')

$TableName = 'TestTable'

_SQLite_Exec(-1, "CREATE TABLE IF NOT EXISTS '" & $TableName & "' (Datestamp string, Counter1 int, Counter2 double, Data blob);")

Local $Data[100000][5]
For $i = 0 to 99999
	$Data[$i][0] = "teststring"
	$Data[$i][1] = $i*2
	$Data[$i][2] = $i/3
	$Data[$i][3] = Binary("This is string in BLOB with 3 trailing zeroes." & Chr(0) & Chr(0) & Chr(0))
Next
$hQuery = 0

;----------------------------------------------------------------------------------------------
$t = TimerInit()

_SQLite_Exec(-1, 'BEGIN TRANSACTION;')
_SQLite_Query(-1, "INSERT INTO '" & $TableName & "' VALUES (?, ?, ?, ?);",$hQuery)
For $i = 0 To UBound($Data)-1
	_SQLite_BindText($hQuery, 1, $Data[$i][0])
	_SQLite_BindInt($hQuery, 2, $Data[$i][1])
	_SQLite_BindDouble($hQuery, 3, $Data[$i][2])
	_SQLite_BindBlob($hQuery, 4, $Data[$i][3])
	_SQLite_Step($hQuery)
	_SQLite_ClearBindings($hQuery)
	_SQLite_QueryReset($hQuery)
Next
_SQLite_QueryFinalize($hQuery)
_SQLite_Exec(-1, 'END TRANSACTION;')

$dt1 = TimerDiff($t)
;----------------------------------------------------------------------------------------------
_SQLite_Exec(-1, "DROP TABLE IF EXISTS '" & $TableName & "';")
_SQLite_Exec(-1, "CREATE TABLE IF NOT EXISTS '" & $TableName & "' (Datestamp string, Counter1 int, Counter2 double, Data blob);")
;----------------------------------------------------------------------------------------------
$t = TimerInit()

_FillTableEX()

$dt2 = TimerDiff($t)
;----------------------------------------------------------------------------------------------
MsgBox(64,"","Bind-insert  = " & @TAB & Round($dt1) & " ms" & @crlf & "Chain-insert = " & @TAB & Round($dt2) & " ms" & @crlf & "Bind / Chain = " & @TAB & Round($dt1/$dt2*100,2) & " %")

_SQLite_Close()
_SQLite_Shutdown()

Func _FillTableEX()
	Local $sSqlBuild_Query = "", $k = 0, $iCount = UBound($Data), $n = Int($iCount/100)
	For $i = 0 To $iCount-1 Step $n
		$k = (($i+$n) > $iCount) ? ($iCount-$i-2) : ($n-2)
		$sSqlBuild_Query &= "INSERT INTO '" & $TableName & "' (Datestamp, Counter1, Counter2, Data) VALUES "
		For $j = 0 To $k
			$sSqlBuild_Query &= "('" & StringReplace( $Data[$i+$j][0], "'", "''") & "'"
			$sSqlBuild_Query &= "," & $Data[$i+$j][1]
			$sSqlBuild_Query &= "," & $Data[$i+$j][2]
			$sSqlBuild_Query &= ",'" & $Data[$i+$j][3] & "'),"
		Next
		$sSqlBuild_Query &= "('" & StringReplace( $Data[$i+$j][0], "'", "''") & "'"
		$sSqlBuild_Query &= "," & $Data[$i+$k+1][1]
		$sSqlBuild_Query &= "," & $Data[$i+$k+1][2]
		$sSqlBuild_Query &= ",'" & $Data[$i+$k+1][3] & "');"
	Next
	_SQLite_Exec(-1, $sSqlBuild_Query )
EndFunc
 
Автор
veretragna

veretragna

Как писал, так и работает.
Сообщения
140
Репутация
10
Naisho
Могу предположить, что время вставки зависит от набора данных.
Я проверил Ваш скрипт еще раз на другом компьютере с быстрым жестким диском (WD Black).
Ваш алгоритм и вправду быстрее на 59% за мой при вставке тестового массива, время вставки составляет соответственно 32720 и 20513 мс.
Однако на рабочем проекте, где в последний параметр привязываются blob'ы размером 3 кб, мой алгоритм быстрее на 60%, время вставки составило 20884 и 33590 мс. Выборку я взял в 40 тысяч элементов. Почему так мало? А потому, что при большем количестве данных базу раздувает, как на дрожжах. 40 тысяч элементов - это 120 мб, плюс-минус.

Резюмируя вышесказанное:
* мелкие данные - выгоднее использовать способы товарищей madmasles или Naisho.
* крупные фрагменты данных/blob'ов - мой способ (биндинг) подходит лучше.
Или нужно провести еще ряд тестов, но мне лениво.

Оу, и кстати говоря, у Вас часом нет ошибки? Для вставки в таблицу blob'ов нужно выполнять _SQLite_FastEncode().
 
Верх