標題:
[發問]
excel vba (插入資料)insert 進 sqlite 緩慢問題
[打印本頁]
作者:
joey0415
時間:
2013-8-6 22:16
標題:
excel vba (插入資料)insert 進 sqlite 緩慢問題
本帖最後由 joey0415 於 2013-8-6 22:21 編輯
之前曾經練習insert把資料從sheet1中,放進sqlite資料表中,
後來在sheet1中加入多行資料,也想將數十從的資料透過for 回圈放進資料資料表中
雖然按下去也可以執行,但是速度慢到無法想像,才500筆的資料就要數十秒之多
[attach]15715[/attach]
我的表(home)
[attach]15716[/attach]
上網查過資料,發現sqlite若要大量insert時,可以透過begin 與commit的方法,先打開(begin),再一起sql語法,最後一次寫入(commit)
下面是小弟網路上查到的vba語法,看樣子是對的,不過還是會報錯,最後一個是小弟可以成功,但是速度慢的方法,不知道可以幫忙修改成快的方法嗎?
[attach]15714[/attach]
謝謝
網路上的語法
(有begin commit)
http://www.mrexcel.com/forum/excel-questions/435486-sqlite-excel-visual-basic-applications-4.html
sub Batch_Insert_Statement
For i = 1 To 100000
xsql = xsql & "Insert into tablename (ProductCode, UnitsSold, DateSold) values '" & Trim(MyRecordset!ProductCode) & "', '" & MyRecordset!UnitsSold &"', '" &
Format(MyRecordset!DateSold, "YYYY-MM-DD HH:MM:SS") & "'; "
Next i
do_sql (xsql)
end sub
' Insert records inside a SQLite transaction - Open DB - begin - batch - Commit
Sub do_sql(xsql As String)
Set filecnn = New_c.Connection("C:\PathToYour\Database.sql")
filecnn.Execute "Begin"
filecnn.Execute xsql
filecnn.Execute "Commit"
Set filecnn = Nothing
End Sub
複製代碼
我的做法,但速度慢
[b]Sub vba_sqlite_inser批次成功[/b]()
Dim x, rcnt, i
Dim sql_command As String
Dim connection_string As String
Worksheets(1).Activate
rcnt = Range("a1").CurrentRegion.Rows.Count
i = 1
For i = 1 To rcnt
Set x = CreateObject("adodb.recordset")
' sql_command = "insert into home(name, age) values('mary','50') "
aa = "insert into home(name, age) values('" & Cells(i, 1) & "','" & Cells(i, 2) & "' & "')"
sql_command = aa
connection_string = ("Driver={SQLite3 ODBC Driver};database=D:\joey.sqlite")
x.Open sql_command, connection_string
Set x = Nothing
Next
End Sub
複製代碼
作者:
luhpro
時間:
2013-8-7 00:06
回復
1#
joey0415
有試過用 :
Application.EnableEvents = False
與
Application.EnableEvents = True
包覆處理與顯示期間的程式碼嗎?
作者:
joey0415
時間:
2013-8-7 08:13
回復
2#
luhpro
有呀!不過那不是問題
是sqlite要插入資料時,當然要open與close
每插入一筆就要開關一次,小弟說的是它有一種寫法是
一起打開(begin)
sql (insert XXXXXXX value( XXX,XXX,XXX)
sql (insert XXXXXXX value( XXX,XXX,XXX)
sql (insert XXXXXXX value( XXX,XXX,XXX)
sql (insert XXXXXXX value( XXX,XXX,XXX)
一起提交(commit)
的方式,那種寫法在vba中,我不會寫,所以想請教一下高手指點
謝謝回答
作者:
joey0415
時間:
2013-8-7 16:08
找到解決的方法一,非正式…
sql語句例如
INSERT INTO "表格名" ("欄位1", "欄位2", ...)VALUES ("值1", "值2", ...);
sqlite它允許
INSERT INTO "表格名" ("欄位1", "欄位2", ...)VALUES
("值1", "值2", ...)
,
("值3", "值4", ...)
,
("值5", "值6", ...)
;
的方法,一次最多輸入500組數值,超過後一樣會報錯
如果你有1534組要insert進資料庫中,可以分成四組 34 500 500 500 的方法
分批加進資料庫中
如果是一組組輸入,上例大概要30秒以上
如果是分四組輸入,估不用2秒
以下是個人程式碼:僅供參考
Sub sqlite批次insert多筆成功()
'最多一次500條,要分段放進去
Dim x, rcnt, i
Dim sql_command As String
Dim connection_string As String
Worksheets(1).Activate
rcnt = Range("a1").CurrentRegion.Rows.Count
倍數 = (rcnt \ 500)
zx = rcnt Mod 500 '找500餘數
j = 0
If rcnt < 500 Then '如果小於500時只計算下面資料
aa = "insert into dbs(yyyymmdd, stockname, tradercode, price, buyamount, sellamount) values"
Set x = CreateObject("adodb.recordset")
cc = ""
cc = aa
i = 1
For i = 1 To rcnt
bb = "('" & Cells(i, 1) & "','" & Cells(i, 2) & "','" & Cells(i, 3) & "','" & Cells(i, 4) & "','" & Cells(i, 5) & "','" & Cells(i, 6) & "')"
'串接要insert的數值對
cc = cc & bb & ","
Next
cc = Left(cc, Len(cc) - 1) '取字串並刪除最後一個","
sql_command = cc
connection_string = ("Driver={SQLite3 ODBC Driver};database=D:\股票資料下載\dbs.sqlite")
x.Open sql_command, connection_string
Set x = Nothing
Else '如果大於500時,要算下面兩種資料
aa = "insert into dbs(yyyymmdd, stockname, tradercode, price, buyamount, sellamount) values"
Set x = CreateObject("adodb.recordset")
cc = ""
cc = aa
i = 1
For i = 1 To zx
bb = "('" & Cells(i, 1) & "','" & Cells(i, 2) & "','" & Cells(i, 3) & "','" & Cells(i, 4) & "','" & Cells(i, 5) & "','" & Cells(i, 6) & "')"
cc = cc & bb & ","
Next
cc = Left(cc, Len(cc) - 1)
sql_command = cc
connection_string = ("Driver={SQLite3 ODBC Driver};database=D:\股票資料下載\dbs.sqlite")
x.Open sql_command, connection_string
Set x = Nothing
For j = 0 To 倍數 - 1
aa = "insert into dbs(yyyymmdd, stockname, tradercode, price, buyamount, sellamount) values"
Set x = CreateObject("adodb.recordset")
cc = ""
cc = aa
i = 1
For i = j * 500 + zx + 1 To (j + 1) * 500 + zx
bb = "('" & Cells(i + j * 500, 1) & "','" & Cells(i + j * 500, 2) & "','" & Cells(i + j * 500, 3) & "','" & Cells(i + j * 500, 4) & "','" & Cells(i + j * 500, 5) & "','" & Cells(i + j * 500, 6) & "')"
cc = cc & bb & ","
Next
cc = Left(cc, Len(cc) - 1)
sql_command = cc
connection_string = ("Driver={SQLite3 ODBC Driver};database=D:\股票資料下載\dbs.sqlite")
x.Open sql_command, connection_string
Set x = Nothing
Next
End If
End Sub
複製代碼
作者:
joey0415
時間:
2013-8-7 16:10
如果大大還有更好的方法,請回文讓小弟多學習,這資料可是花了很久的時間才突破的
作者:
koshi0413
時間:
2016-12-25 21:23
如果大大還有更好的方法,請回文讓小弟多學習,這資料可是花了很久的時間才突破的
joey0415 發表於 2013-8-7 16:10
小弟引用你上述的方式,一直跑到第500筆即失敗
以下是小弟的方法,速度好像還好,範列 4000筆,有比你原來的一筆一筆快,但應該比你的一次500筆慢
Sub SQLite_insert()
Sheets("SQL JOIN").Select
SQLName = "D:\Dropbox\SQLite3\Stock.sqlite"
insert = "insert into Stock values"
ret = ""
ret = insert
et = 113 '導入的標題數
rcnt = Range("A1").CurrentRegion.Rows.Count '計算所有的行
xa = 3 '從第三行開始計算
For j = xa To rcnt - 2
zc = ""
For i = 1 To et
za = Cells(j, i)
If za = "" Then '空格轉換
za = 0
ElseIf i = 2 Then '日期轉換
If (Len(Split(za, "/")(1)) = 1) And (Len(Split(za, "/")(2)) = 1) Then
za = Split(za, "/")(0) & "-0" & Split(za, "/")(1) & "-0" & Split(za, "/")(2)
ElseIf Len(Split(za, "/")(1)) = 1 Then
za = Split(za, "/")(0) & "-0" & Split(za, "/")(1) & "-" & Split(za, "/")(2)
ElseIf Len(Split(za, "/")(2)) = 1 Then
za = Split(za, "/")(0) & "-" & Split(za, "/")(1) & "-0" & Split(za, "/")(2)
Else
za = Split(za, "/")(0) & "-" & Split(za, "/")(1) & "-" & Split(za, "/")(2)
End If
End If
zc = zc & "'" & za & "',"
Next i
zc = Left(zc, Len(zc) - 1) '刪除最後一個","
rez = "(" & zc & ")"
ret = ret & rez & ","
Next j
ret = Left(ret, Len(ret) - 1) '刪除最後一個","
SqlImport = ret
Set cn = CreateObject("adodb.connection")
cn.Open ("Driver={SQLite3 ODBC Driver};database=" & SQLName) '開啟sqlite指定資料庫,路徑與檔案名稱要對
cn.Execute (SqlImport)
cn.Close
Set cn = Nothing
End Sub
複製代碼
作者:
koshi0413
時間:
2016-12-25 21:54
回復
6#
koshi0413
剛才試了 7983筆,1分鐘
python的 for 好像比較快 哈哈
可是execl 還是有方便計算的地方
作者:
PKKO
時間:
2016-12-27 12:15
回復
5#
joey0415
小弟用EXCEL寫入mysql 和 mssql 以前也遇到相同的問題
但小弟解決的方式不是挺優,您不嫌棄的話可以試試看
用連接符號=";"
把語法連接起來,EXCUTE的動作等迴圈跑完之後,再給他一次EXCUTE就好了
您的語法主要是慢在數量多,你每一列都打開資料庫連結一次,因此速度慢了
連接在一起就只要打開資料庫一次,一次放入五百筆資料,就非常快了!
Sub WRITE_SQL() '寫入資料
conn_connect'這是呼叫開啟SQL
For i = 1 To 10000
strSQL = "INSERT INTO customer VALUES ('Bob','SHIT','TWN','SHIT2','SHIT3',2016-01-01," & 100 + i & ")"'每一列的語法
If i = 1 Then strSQL2 = strSQL Else strSQL2 = strSQL2 & ";" & strSQL'將語法連接
Next
Conn.Execute (strSQL2)'執行SQL
End Sub
複製代碼
作者:
koshi0413
時間:
2016-12-28 12:44
回復
8#
PKKO
P大
小弟的例子就是用跟您一下的方法
恩 vba跑迴圈是不是比較慢呢?
一次500筆很快
一次7999筆,一分鐘
然後一次性丟入 sqlite3
j大的批次方式,好像就是 7999/500 ,分段的一次性丟入
只是小弟分段丟入都出門,7999/3000 ,也一樣丟不進,
很奇怪,不知道是不是sqlite3反應時間的問題,下次有空, 中間丟個等待好了
不過這樣一來,就跟一次性7999迴圈差不多時間?
要試試~~
作者:
PKKO
時間:
2016-12-28 21:45
本帖最後由 PKKO 於 2016-12-28 21:48 編輯
回復
9#
koshi0413
excel 不會慢到哪邊去哦
我的都是一秒多就結束了(一秒是因為要開啟和關閉資料庫)
您的速度慢是因為妳用了ZA=CELLS(I,J)
excel要快最基本的要件就是先轉為陣列
例如
RNG=[A1].RESIZE(100,10).VALUE'取出100列*10欄位的資料
接著把妳的cells換成RNG就可以了
excel讀取儲存格是非常慢的速度
讀取陣列是非常快的,放資料的時候也一樣哦,不能一個一個放,要一次放一整個陣列進去哦!
以及上方先放入運算的程式,避免每一個if都要執行一次SPLIT
作者:
koshi0413
時間:
2016-12-31 14:31
本帖最後由 koshi0413 於 2016-12-31 14:34 編輯
回復
10#
PKKO
可否請p大協助一下,看小弟的語法是不是有問題
因為在跑 For j = 3 To rcnt << 最多會 3 to 10000 迴圈許久
za = cell(j,i) 是為了空格&字串判別,這好像不這樣的話導入日期都錯誤,還是可以這樣 str("2016/03/03") 這樣轉換成字串
至於導入sqlite3 是這行,開關一下就結束了,目前關卡在 迴圈太久 orz.....
Set cn = CreateObject("adodb.connection")
cn.Open ("Driver={SQLite3 ODBC Driver};database=" & SQLName)
cn.Close
Set cn = Nothing
ps:小弟是程式白痴,全自學的, python比較順手
Sub SQLite_insert()
Sheets("SQL JOIN").Select
SQLName = "D:\Dropbox\SQLite3\StockAll.sqlite"
insert = "insert into StockAll values"
ret = ""
ret = insert
et = 114 '導入的標題數
rcnt = Range("A1").CurrentRegion.Rows.Count '計算所有的行
For j = 3 To rcnt
zc = ""
For i = 1 To et
za = Cells(j, i)
If za = "" Then '空格轉換
za = 0
ElseIf i = 2 Then '日期轉換
If (Len(Split(za, "/")(1)) = 1) And (Len(Split(za, "/")(2)) = 1) Then
za = Split(za, "/")(0) & "-0" & Split(za, "/")(1) & "-0" & Split(za, "/")(2)
ElseIf Len(Split(za, "/")(1)) = 1 Then
za = Split(za, "/")(0) & "-0" & Split(za, "/")(1) & "-" & Split(za, "/")(2)
ElseIf Len(Split(za, "/")(2)) = 1 Then
za = Split(za, "/")(0) & "-" & Split(za, "/")(1) & "-0" & Split(za, "/")(2)
Else
za = Split(za, "/")(0) & "-" & Split(za, "/")(1) & "-" & Split(za, "/")(2)
End If
End If
zc = zc & "'" & za & "',"
Next i
zc = Left(zc, Len(zc) - 1) '刪除最後一個","
rez = "(" & zc & ")"
ret = ret & rez & ","
DoEvents
Next j
'MsgBox "跑完迴圈"
ret = Left(ret, Len(ret) - 1) '刪除最後一個","
SqlImport = ret
Set cn = CreateObject("adodb.connection")
cn.Open ("Driver={SQLite3 ODBC Driver};database=" & SQLName)
cn.Close
Set cn = Nothing
End Sub
複製代碼
作者:
koshi0413
時間:
2016-12-31 15:30
p大,
('Bob','SHIT','TWN','SHIT2','SHIT3',2016-01-01," & 100 + i & ")" << 這行是小弟看不懂的地方
恩~~為何看不懂,是因為小弟要逐行120個儲存格皆寫入,
還是要寫成 strSQL = "INSERT INTO customer VALUES (Range("A" & i),Range("B" & i),Range("C" & i),.......共120個.......," & 100 + i & ")"
我好像觀念怪怪的@@
Sub WRITE_SQL() '寫入資料
conn_connect'這是呼叫開啟SQL
For i = 1 To 10000
strSQL = "INSERT INTO customer VALUES ('Bob','SHIT','TWN','SHIT2','SHIT3',2016-01-01," & 100 + i & ")" '每一列的語法
If i = 1 Then
strSQL2 = strSQL
Else
strSQL2 = strSQL2 & ";" & strSQL'將語法連接
End if
Next
Conn.Execute (strSQL2)'執行SQL
End Sub
複製代碼
作者:
PKKO
時間:
2016-12-31 16:27
回復
12#
koshi0413
ZA=cells(i,j)
先把CELLS換成陣列
後面的ZA都不用動,就已經快很多了
意思是一樣的
VBA強制轉換文字的指令是CSTR(XXX)
100多個儲存格也是一樣
先一次性轉為陣列
然後跑回圈
妳如果沒有轉陣列用range("A" & i) 大概會慢到想要打人吧!
作者:
koshi0413
時間:
2016-12-31 21:39
回復
13#
PKKO
哇~~新年快樂,沒想到跨年夜還回文
非常謝謝唷,小弟先改來試一下,如果還慢,在上來請教~~~
所以 cells(i,j) 速度 > Range("A" & i) 這點小弟倒是不知道,還以為一樣呢
作者:
koshi0413
時間:
2016-12-31 23:42
回復
13#
PKKO
P大,小弟單獨弄了個迴圈來試,一樣要一分鐘也?
是小弟弄錯了嘛
for i = 1 to 10000
cells(i.2) = i
next i
要如何改成陣列呢?
作者:
koshi0413
時間:
2017-1-1 01:19
回復
15#
koshi0413
找到教學了,謝謝P大指點^_^
https://dotblogs.com.tw/yc421206/archive/2008/12/20/6470.aspx
作者:
koshi0413
時間:
2017-1-1 14:31
回復
13#
PKKO
p大,今天試了一下,也不知道對不對
一樣 80秒,好像都是卡在迴圈,是不是小弟用法弄錯啦,
https://dotblogs.com.tw/yc421206/archive/2008/12/20/6470.aspx <<但是照這個網頁的範例跑是非常快的(16秒),疑問
如果可以的話,可以指導一下嘛?您說的 za=cells(j,i) 改成陣列,實在是試不出來
myCols = Range("A2").CurrentRegion.Columns.Count '計算所有的列
myRows = Range("A1").CurrentRegion.Rows.Count '計算所有的行
ReDim myArray(1 To myRows, 1 To myCols) '動態陣列
For j = 3 To myRows
zc = ""
For i = 1 To myCols
myArray(j, i) = Cells(j, i)
za = myArray(j, i)
If za = "" Then '空格轉換
za = 0
ElseIf i = 2 Then '日期轉換
za = Replace(za, "/", "-")
End If
zc = zc & "'" & za & "',"
Next i
zc = Left(zc, Len(zc) - 1) '刪除最後一個","
rez = "(" & zc & ")"
ret = ret & rez & ","
DoEvents
Next j
複製代碼
作者:
PKKO
時間:
2017-1-1 23:14
回復
17#
koshi0413
用法錯囉,你一樣是讀取了cells的值
rng=[a1].resize(r,c).value'r=列數,C=欄位數量
之後把CELLS的部分取代為rng就可以用陣列了
例如原本要取用cells(2,1) 就變成 rng(2,1)
意思完全相同,但不是取用儲存格而是取用陣列,速度直接大增哦!
作者:
koshi0413
時間:
2017-1-1 23:46
回復
18#
PKKO
果然用法錯了@@
謝謝 p大指導,今天一氣之下弄了python來導入了,約10秒
明天再來試vba陣列,非常感謝唷!!!!!!
作者:
koshi0413
時間:
2017-1-2 01:15
回復
18#
PKKO
P大,哎呀呀,抱歉咧
應該又試錯了,一樣80秒@@
改成下面這樣是72秒,所以 if 內日期轉換需 8秒
myCols = 123
myRows = 7923
Rng = [A3].Resize(myRows, myCols).Value
For j = 1 To (myRows - 2)
zc = ""
For i = 1 To myCols
za = Rng(j, i)
zc = zc & "'" & za & "',"
Next i
zc = Left(zc, Len(zc) - 1)
rez = "(" & zc & ")"
ret = ret & rez & ","
DoEvents
Next j
Application.ScreenUpdating = False
StartTime = Timer
myCols = Range("A2").CurrentRegion.Columns.Count '計算所有的列
myRows = Range("A1").CurrentRegion.Rows.Count '計算所有的行
Rng = [A3].Resize(myRows, myCols).Value
For j = 1 To (myRows - 2)
zc = ""
For i = 1 To myCols
za = Rng(j, i)
If za = "" Then '空格轉換
za = 0
ElseIf i = 2 Then '日期轉換
If (Len(Split(za, "/")(1)) = 1) And (Len(Split(za, "/")(2)) = 1) Then
za = Split(za, "/")(0) & "-0" & Split(za, "/")(1) & "-0" & Split(za, "/")(2)
ElseIf Len(Split(za, "/")(1)) = 1 Then
za = Split(za, "/")(0) & "-0" & Split(za, "/")(1) & "-" & Split(za, "/")(2)
ElseIf Len(Split(za, "/")(2)) = 1 Then
za = Split(za, "/")(0) & "-" & Split(za, "/")(1) & "-0" & Split(za, "/")(2)
Else
za = Split(za, "/")(0) & "-" & Split(za, "/")(1) & "-" & Split(za, "/")(2)
End If
End If
zc = zc & "'" & za & "',"
Next i
zc = Left(zc, Len(zc) - 1) '刪除最後一個","
rez = "(" & zc & ")"
ret = ret & rez & ","
DoEvents
Next j
EndTime = Timer
MsgBox Format(EndTime - StartTime, "00.00") & "秒"
Application.ScreenUpdating = True
複製代碼
作者:
PKKO
時間:
2017-1-4 16:52
回復
20#
koshi0413
我沒有很深入的看您細節的程式碼
但目前只有看到兩個地方可以稍微快一點,你可以試試看這個部分
第一:za=RNG(J,I) =>這個部分可以不用,只接把所有的za取代為RNG(J,I)即可=>但這個地方影響不大
第二:您的迴圈之內使用了大量的SPLIT,應該在迴圈開始的時候直接將AR=Split(za, "/")
然後IF AR(0)=1 OR LEN(AR(1))=1 THEN
這樣的話程式就不用每一行都執行一次拆開的動作
以上您試試看
作者:
koshi0413
時間:
2017-1-4 18:44
回復
21#
PKKO
謝謝p大指點,下星期出差大陸時,來試跑~~到時在回報
不過您說的修改第二點,大蓋省8秒吧,這樣還是要70秒,個人覺的沒道理@@
歡迎光臨 麻辣家族討論版版 (http://forum.twbts.com/)