C++操作SQLITE获得查询结果集的几种方法总结

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C++操作SQLITE获得查询结果集的⼏种⽅法总结
原⽂链接:
使⽤sqlite的时候对查询结果的获得⼀直感觉⽐较混乱,⼀通google后收益匪浅,在此做个笔记。

⽅式⼀:利⽤回调函数callback
回调函数格式:
int callback(void para , int nCount, char** pValue, char** pName) {
/*****************************************************************************
sqlite 每查到⼀条记录,就调⽤⼀次这个回调
para是你在 sqlite3_exec ⾥传⼊的 void * 参数, 通过para参数,你可以传⼊⼀些特殊的指针(⽐如类指 针、结构指针),然后在这⾥⾯强制转换成对应的类型(这⾥⾯是void类型,必须强制转换成你的类型才可⽤)。

然后操作这些数据
nCount是这⼀条(⾏)记录有多少个字段 (即这条记录有多少列)
char ** pValue 是个关键值,查出来的数据都保存在这⾥,它实际上是个1维数组(不要以为是2维数组),每⼀个元素都是⼀个 char*值,是⼀个字段内容(⽤字符串来表⽰,以/0结尾)
char ** pName 跟pValue是对应的,表⽰这个字段的字段名称, 也是个1维数组
****************************************************************************/
string s;
for(int i=0;i<nCount;i++){
s+=pName[i];
s+=":";
s+=pValue[i];
s+="\n";
}
cout<<s<<endl;
return 0;
}
执⾏sql语句:
int sqlite3_exec(sqlite3, const char *sql,sqlite3_callback, void ,char errmsg);
第1个参数不再说了,是前⾯open函数得到的指针。

说了是关键数据结构。

第2个参数constchar sql 是⼀条 sql 语句,以/0结尾。

第3个参数sqlite3_callback 是回调,当这条语句执⾏之后,sqlite3会去调⽤你提供的这个函数。

(若是封装成类的,必须将回调函数设置为static,否则this指针不对应⽆法调⽤)
第4个参数void 是你所提供的指针,你可以传递任何⼀个指针参数到这⾥,这个参数最终会传到回调函数⾥⾯,如果不需要传递指针给回调函数,可以填NULL。

等下我们再看回调函数的写法,以及这个参数的使⽤。

第5个参数char errmsg 是错误信息。

注意是指针的指针。

sqlite3⾥⾯有很多固定的错误信息。

执⾏ sqlite3_exec 之后,执⾏失败时可以查阅这个指针(直接 printf(“%s/n”,errmsg))得到⼀串字符串信息,这串信息告诉你错在什么地⽅。

sqlite3_exec函数通过修改你传⼊的指针的指针,把你提供的指针指向错误提⽰信息,这样sqlite3_exec函数外⾯就可以通过这个 char得到具体错误提⽰。

⽅式⼆:sqlite3_get_table
函数分析:
int sqlite3_get_table(sqlite3*, const char *sql, char ***resultp, int *nrow, int *ncolumn, char **errmsg);
第1个参数不再多说,看前⾯的例⼦。

第2个参数是 sql 语句,跟 sqlite3_exec ⾥的 sql 是⼀样的。

是⼀个很普通的以/0结尾的char *字符串。

第3个参数是查询结果,它依然⼀维数组(不要以为是⼆维数组,更不要以为是三维数组)。

它内存布局是:resultp的字段值是连续的,第⼀⾏是字段名称,后⾯是紧接着是每个字段的值。

从第0索引到第 nColumn - 1索引都是字段名称,从第nColumn 索引开始,后⾯都是字段值,它把⼀个⼆维的表(传统的⾏列表⽰法)⽤⼀个扁平的形式来表⽰。

//第n列的名称,存放于resultp [nrow]
//第n⾏第m列的数据,存放于resultp [(nrow+ 1) * nColumn + m]
下⾯⽤例⼦来说事。

第4个参数是查询出多少条记录(即查出多少⾏)。

第5个参数是多少个字段(多少列)。

第6个参数是错误信息。

Eg:
string strSql = “select * from MyTable”;
char** pResult;
int nRow;
int nCol;
nResult = sqlite3_get_table(db,strSql.c_str(),&pResult,&nRow,&nC,&errmsg);
if (nResult != SQLITE_OK)
{
sqlite3_close(db);
cout<<errmsg<<endl;
sqlite3_free(errmsg);
return 0;
}
string strOut;
int nIndex = nCol;
for(int i=0;i<nRow;i++)
{
for(int j=0;j<nCol;j++)
{
strOut+=pResult[j];
strOut+=":";
strOut+=pResult[nIndex];
strOut+="\n";
++nIndex;
}
}
sqlite3_free_table(pResult); //使⽤完后务必释放为记录分配的内存,否则会内存泄漏
cout<<strOut<<endl;
sqlite3_close(db);
return 0;
}
⽅式三:通过sqlite3_prepare、sqlite3_step⼀系列操作
虽然回调显得代码整齐,⼜想避免sqlite3_get_table之后⿇烦的⼀维数组遍历,那么利⽤sqlite3_prepare_v2执⾏sql select语句,让后sqlite3_step遍历select执⾏的返回结果是⼀个⾮常⽅便的solution. 当然,你必须要明⽩sqlite3_prepare_v2不仅仅能够执⾏table的query selection,也能⽅便地进⾏sql Delete, Insert, Update等其他⼀些操作。

它能帮你把sql语句的执⾏操作变的更加优雅。

相关函数分析:
1. int sqlite3_prepare_v2(
sqlite3db, / Database handle(数据库指针) /
const char zSql, /* SQL statement, UTF-8encoded(使⽤UTF-8编码的SQL语句) /
int nByte, / Maximum length of zSql inbytes.(如果nByte⼩于0,则函数取出zSql中从开始到第⼀个0终⽌符的内容;如果nByte 为⾮负数,那么它就是这个函数能从zSql中读取的最⼤字节数。

zSql在第⼀次遇见/000或u000的时候终⽌) */
sqlite3_stmt *ppStmt, / OUT:Statement handle(能够使⽤sqlite3_step()执⾏的编译好的准备语句的指针,如果错误发⽣,它被置为NULL,例如输⼊⼀个不正确的sql语句。

调⽤过程必须负责在编译好的sql语句完成使⽤后,调⽤sqlite3_finalize()删除它。

/ const char**pzTail / OUT: Pointer to unusedportion of zSql(当zSql在遇见终⽌符或者达到设定的nByte结束后,如果还有剩余的内容,那么这些剩余的内容将被存放到pzTail中,不包含终⽌符) */
);
2. int sqlite3_step(sqlite3_stmt*);
sqlite3_step的返回值取决于创建sqlite3_stmt参数所使⽤的函数,假如使⽤⽼版本的接⼝sqlite3_prepare()或
sqlite3_prepare16(),返回值会是SQLITE_BUSY、SQLITE_DONE, SQLITE_ROW, SQLITE_ERROR 或 SQLITE_MISUSE;
⽽v2版本的接⼝sqlite3_prepare_v2()和sqlite3_prepare16_v2()除了这些值以外,还可能返回扩展状态码。

3 . int sqlite3_reset(sqlite3_stmt *pStmt);
sqlite3_reset 重置⼀个准备语句(prepared statement)对象到它的初始状态,准备被重新执⾏。

在V3.6.23.1以后,sqlite3_step()将会⾃动调⽤sqlite3_reset。

int sqlite3_finalize(sqlite3_stmt *pStmt);
sqlite3_finalize — 销毁⼀个准备语句(prepared statement)对象,在需要时执⾏这个销毁函数以防⽌内存泄露。

在准备语句对象为空指针时调⽤这个函数也没有什么影响。

4 . sqlite3_column —并不存在sqlite3_column这个函数,它只是⼀个前缀,下⾯是相关的具体函数:
const void sqlite3_column_blob(sqlite3_stmt, int
intsqlite3_column_bytes(sqlite3_stmt*, int iCol);
intsqlite3_column_bytes16(sqlite3_stmt*, int iCol);
doublesqlite3_column_double(sqlite3_stmt*, int iCol);
intsqlite3_column_int(sqlite3_stmt*, int iCol);
sqlite3_int64sqlite3_column_int64(sqlite3_stmt*, int
constunsigned char sqlite3_column_text(sqlite3_stmt,
const void sqlite3_column_text16(sqlite3_stmt, int
intsqlite3_column_type(sqlite3_stmt*, int iCol);
sqlite3_value sqlite3_column_value(sqlite3_stmt, int iCol);
上⾯的函数⽤来得到结果集当前⾏中某⼀列的值,列索引从0开始。

注意点:
1. 调⽤sqlite3_prepare()将SQL语句编译为sqlite内部⼀个结构体(sqlite3_stmt).该结构体中包含了将要执⾏的的SQL语句的信息.
2. 如果需要传⼊参数,在SQL语句中⽤’?’作为占位符,再调⽤sqlite3_bind_XXX()函数将对应的参数传⼊.
3. 调⽤sqlite3_step(),这时候SQL语句才真正执⾏.注意该函数的返回值,SQLITE_DONE和SQLITE_ROW都是表⽰执⾏成功,不同的是
SQLITE_DONE表⽰没有查询结果,像UPDATE,INSERT这些SQL语句都是返回SQLITE_DONE,SELECT查询语句在查询结果不为空的时候返回SQLITE_ROW,在查询结果为空的时候返回SQLITE_DONE.(SQLITE_DONE尽管被归结为error code,但是不代表着执⾏错误,⽽是意味着查询结果为空。

SQLITE_OK表⽰执⾏结束并有查询结果。


4. 每次调⽤sqlite3_step()的时候,只返回⼀⾏数据,使⽤sqlite3_column_XXX()函数来取出这些数据.要取出全部的数据需要反复调⽤
sqlite3_step(). (注意,在bind参数的时候,参数列表的index从1开始,⽽取出数据的时候,列的index是从0开始).
5. 在SQL语句使⽤完了之后要调⽤sqlite3_finalize()来释放stmt占⽤的内存.该内存是在sqlite3_prepare()时分配的.
6. 如果SQL语句要重复使⽤,可以调⽤sqlite3_reset()来清楚已经绑定的参数.
7. 对于新的应⽤不建议使⽤sqlite3_prepare(),⽽应使⽤架构更新的程序sqlite3_prepare_v2()代替。

8. 对于很多SQL语句来说,执⾏sqlite3_prepare()的时间等于或者超过执⾏sqlite3_step()的时间。

所以避免频繁调⽤
sqlite3_prepare()可以显著提升性能。

实例:
Eg1:
//查询所有数据
sqlite3 *db;
sqlite3_stmt * stmt = NULL;
const char *zTail;
int r =sqlite3_open(“mysqlite.db”,&db) //打开数据库
if (sqlite3_prepare_v2(db,
“SELECT ID, name, num FROM players;”, -1, &stmt, &zTail) ==SQLITE_OK){
while( sqlite3_step(stmt) ==SQLITE_ROW ) {
int id =sqlite3_column_int( stmt, 0);
constunsigned char * name = sqlite3_column_text( stmt,1);
intnumber = sqlite3_column_int( stmt, 2);
printf(“ID: %d Name: %s Age: %d /n”,id,name,number);
}
}
sqlite3_finalize(stmt);
//关闭数据库
sqlite3_close(db);
⽅式四:利⽤事务(BEGIN;COMMIT;)批量操作
如果要进⾏⼤量的操作,⽐如要插⼊10000条数据,如果逐条执⾏SQL语句,则消耗的时间⾮常长。

采⽤事务的⽅式批量处理,可以极⼤程度提升操作速度(我⽤1000条记录实验了⼀下,速度提⾼了500倍以上)。

sqlite3_exec可以执⾏任何sql语句,包括事务(“BEGIN TRANSACTION”)、回滚(“ROLLBACK”)和提交(“COMMIT”)等等
Eg1.
//插⼊条数据(在Begin和Commit之间批量操作,可以⼤幅度提⾼效率)
result =sqlite3_exec(db, “BEGIN;”,0, 0, 0);
for (int i=0; i<10000; i++)
{
//插⼊⼀条数据
result = sqlite3_exec(db,
“INSERT INTOMyTable (MyText, MyDate, MyTime, MyFloat) VALUES (’—上班好远!’, ‘2012-03-23’, ‘9:00:00’, 1000);”,
0, 0, 0);
}
result =sqlite3_exec(db, “COMMIT;”,0, 0, 0); .
Eg2.
string strSql;
strSql+=“begin;\n”;
for (int i=0;i<100;i++)
{
strSql+=“insert into MyTable values(null,‘heh’);\n”;
}
strSql+=“commit;”;
//cout<<strSql<<endl;
nResult = sqlite3_exec(db,strSql.c_str(),NULL,NULL,&errmsg);
总结:sqlite3_exec()接⼝是⼀个⽅便的包装器,调⽤⼀个⽅法便可执⾏上⾯的四个步骤。

传递到sqlite3_exec()中的回调函数将⽤于处理每⾏结果集。

sqlite3_get_table()是另⼀个⽅便的包装器,同样可以⽤上述的四个步骤。

与 sqlite3_exec()不同的
是,sqlite3_get_table()将结果存储在堆存储器⾥⽽⾮调⽤回调函数。

相关文档
最新文档