MFC使用SQLite 学习系列 二:无法容忍的数据插入效率
上一篇随笔中,介绍了,基本的使用没什么问题了,那么开始数据的插入。
一 问题--无法容忍的插入效率
代码写入基本完成,然后开始测试。起初,插入数据的时候基本上是插入每次插入9组数据,看不出来数据插入的速度。
然后,当我每次插入600组数据的时候,我的天,这个速度真的无法容忍啊。
插入数据的方式是使用for循环,每次插入一组数据。
for (int i = 0; i < DataArray.GetCount(); i ++) { progressDlg->ProGress.SetPos(i); szQuery.Format("INSERT INTO %s VALUES ('%d','%0.2f','%0.5f','%0.5f','%0.1f','%0.5f','%0.5f','%0.2f','%0.2f','%0.2f')",strTemp,i+1,DataArray.GetAt(i).L,DataArray.GetAt(i).x,DataArray.GetAt(i).y,DataArray.GetAt(i).Tc,DataArray.GetAt(i).u,DataArray.GetAt(i).v,DataArray.GetAt(i).X,DataArray.GetAt(i).Y,DataArray.GetAt(i).Z); fTest = sqlite.DirectStatement(szQuery); if (!fTest) AfxMessageBox(_T("插入数据失败!")); }
我的天,大概感受了一下,600组数据,最起码一分钟以上,而我原来使用文本记录的时候,开始数据少的时候,也不过一秒钟100组数据,可换成这SQLite,600组数据居然要一分多钟。
增加个时间来计下时间:
SYSTEMTIME timeStart; SYSTEMTIME timeStop; GetLocalTime(&timeStart); for (int i = 0; i < DataArray.GetCount(); i ++) { progressDlg->ProGress.SetPos(i); szQuery.Format("INSERT INTO %s VALUES ('%d','%0.2f','%0.5f','%0.5f','%0.1f','%0.5f','%0.5f','%0.2f','%0.2f','%0.2f')",strTemp,i+1,DataArray.GetAt(i).L,DataArray.GetAt(i).x,DataArray.GetAt(i).y,DataArray.GetAt(i).Tc,DataArray.GetAt(i).u,DataArray.GetAt(i).v,DataArray.GetAt(i).X,DataArray.GetAt(i).Y,DataArray.GetAt(i).Z); fTest = sqlite.DirectStatement(szQuery); if (!fTest) AfxMessageBox(_T("插入数据失败!")); } GetLocalTime(&timeStop); printf("\n开始时间 %d:%d:%d",timeStart.wMinute,timeStart.wSecond,timeStart.wMilliseconds); printf("\n结束时间 %d:%d:%d",timeStop.wMinute,timeStop.wSecond,timeStop.wMilliseconds); printf("\n用时%d毫秒\n\n",(timeStop.wMinute*60*1000 + timeStop.wSecond*1000 + timeStop.wMilliseconds)-(timeStart.wMinute*60*1000 + timeStart.wSecond*1000 + timeStart.wMilliseconds));
经过准确的计时,得到600组数据插入的时间是63024毫秒,我的天,再逗我吗。
二 解决方案 事务
经过查找资料,了解到,提升插入效率的方法,就是不要一条条的插入数据,启用一个事务,一起插入。
所谓”事务“就是指一组SQL命令,这些命令要么一起执行,要么都不被执行。在SQLite中,每调用一次sqlite3_exec()函数,就会隐式地开启了一个事务,如果插入一条数据,就调用该函数一次,事务就会被反复地开启、关闭,会增大IO量。如果在插入数据前显式开启事务,插入后再一起提交,则会大大提高IO效率,进而加数据快插入速度。
开启事务只需在上述代码的前后各加一句开启与提交事务的命令即可:
使用SQLite的API函数的话,就是这个样子
sqlite3_exec(db,"begin;",0,0,0); for(int i=0;i<nCount;++i) { std::stringstream ssm; ssm<<"insert into t1 values("<<i<<","<<i*2<<","<<i/2<<","<<i*i<<")"; sqlite3_exec(db,ssm.str().c_str(),0,0,0); } sqlite3_exec(db,"commit;",0,0,0);
那么使用SQLite3Wrapper的封装类该怎么办呢。
只需要插入前后增加一个Begin() 和 Commit()即可,就是这个样子:
sqlite.Commit(); GetLocalTime(&timeStop); printf("\n开始时间 %d:%d:%d",timeStart.wMinute,timeStart.wSecond,timeStart.wMilliseconds); printf("\n结束时间 %d:%d:%d",timeStop.wMinute,timeStop.wSecond,timeStop.wMilliseconds); printf("\n用时%d毫秒\n\n",(timeStop.wMinute*60*1000 + timeStop.wSecond*1000 + timeStop.wMilliseconds)-(timeStart.wMinute*60*1000 + timeStart.wSecond*1000 + timeStart.wMilliseconds)); progressDlg->OnClose(); SYSTEMTIME timeStart; SYSTEMTIME timeStop; GetLocalTime(&timeStart); sqlite.Begin(); for (int i = 0; i < DataArray.GetCount(); i ++) { progressDlg->ProGress.SetPos(i); szQuery.Format("INSERT INTO %s VALUES ('%d','%0.2f','%0.5f','%0.5f','%0.1f','%0.5f','%0.5f','%0.2f','%0.2f','%0.2f')",strTemp,i+1,DataArray.GetAt(i).L,DataArray.GetAt(i).x,DataArray.GetAt(i).y,DataArray.GetAt(i).Tc,DataArray.GetAt(i).u,DataArray.GetAt(i).v,DataArray.GetAt(i).X,DataArray.GetAt(i).Y,DataArray.GetAt(i).Z); fTest = sqlite.DirectStatement(szQuery); if (!fTest) AfxMessageBox(_T("插入数据失败!")); } sqlite.Commit(); GetLocalTime(&timeStop); printf("\n开始时间 %d:%d:%d",timeStart.wMinute,timeStart.wSecond,timeStart.wMilliseconds); printf("\n结束时间 %d:%d:%d",timeStop.wMinute,timeStop.wSecond,timeStop.wMilliseconds); printf("\n用时%d毫秒\n\n",(timeStop.wMinute*60*1000 + timeStop.wSecond*1000 + timeStop.wMilliseconds)-(timeStart.wMinute*60*1000 + timeStart.wSecond*1000 + timeStart.wMilliseconds));
经过计时输出,600组数据输出的时间大概是329毫秒,这提升那不是一个等级的啊。当然,这个速度对我目前的数据来说,已经够用了。