实验代码
#include <stdio.h> #include <stdlib.h> #include <sqlite3.h> #include <string.h> //sqlite3的回调函数 sqlite 每查到一条记录,就调用一次这个回调 // 传递的私有参数 一行有多少字段 字段值 字段名 int LoadMyInfo( void * para, int n_column, char ** column_value, char ** column_name ) { //para是你在 sqlite3_exec 里传入的 void * 参数 //通过para参数,你可以传入一些特殊的指针(比如类指针、结构指针),然后在这里面强制转换成对应的类型 //(这里面是void*类型,必须强制转换成你的类型才可用)。然后操作这些数据 //n_column是这一条记录有多少个字段 (即这条记录有多少列) // char ** column_value 是个关键值,查出来的数据都保存在这里,它实际上是个1维数组(不要以为是2维数组), //每一个元素都是一个 char * 值,是一个字段内容(用字符串来表示,以\0结尾) //char ** column_name 跟 column_value是对应的,表示这个字段的字段名称 int i; printf("This is %d column\n", n_column ); for( i = 0 ; i < n_column; i ++ ) { printf( "column name is :%s --->column value is :%s\n", column_name[i], column_value[i] ); if(para != NULL)//当传递的指针不为空的时候,拷贝信息 strcpy((char *)para,column_value[i]); } printf( "------------------\n"); return 0; } int main( int argc, char **argv ) { sqlite3 * db; int result; char * errmsg = NULL; char msg[50]; char ** dbResult; int nRow=0,nColumn=0; int i,j; //int sqlite3_open( 文件名, sqlite3 ** ); result = sqlite3_open("exec2.db", &db ); if( result != SQLITE_OK ) { //数据库打开失败 return -1; } //数据库操作代码 //创建一个测试表,表名叫 MyTable_1,有2个字段: ID 和 name。其中ID是一个自动增加的类型,以后insert时可以不去指定这个字段,它会自己从0开始增加 result = sqlite3_exec( db, "create table MyTable(ID integer primary key autoincrement, name nvarchar(32))", NULL, NULL, &errmsg ); if(result != SQLITE_OK ) { printf("create table failed The error number is %d , Error message is %s\n ", result, errmsg ); } //插入一些记录 result = sqlite3_exec( db, " insert into MyTable( name ) values (' on foot ')", NULL, NULL, &errmsg ); if(result != SQLITE_OK ) { printf("Insert infor failed The error number is %d , Error message is %s\n ", result, errmsg ); } result = sqlite3_exec( db," insert into MyTable( name ) values (' by bike ')", NULL, NULL,&errmsg ); if(result != SQLITE_OK ) { printf("Insert infor failed The error number is %d , Error message is %s\n ", result, errmsg ); } result = sqlite3_exec( db," insert into MyTable( name ) values (' by car ')", NULL, NULL, &errmsg ); if(result != SQLITE_OK ) { printf("Insert infor failed The error number is %d , Error message is %s\n ", result, errmsg ); } //int sqlite3_exec(sqlite3*, const char *sql, sqlite3_callback, void *, char **errmsg ); //开始查询数据库 ,有多少条数据将会被回调多少次 result = sqlite3_exec( db, " select * from MyTable ", LoadMyInfo, NULL, &errmsg ); //通过回调函数进行查询并且返回,查询值 result = sqlite3_exec( db,"select * from MyTable where ID=2",LoadMyInfo,msg,&errmsg); printf("The value of ID=2 is %s\n",msg); result=sqlite3_get_table(db,"select * from MyTable",&dbResult,&nRow,&nColumn,&errmsg); if( errmsg != NULL){ sqlite3_free_table(dbResult); errmsg = NULL; } for(i=0;i<nRow;i++) { for(j=0;j<nColumn;j++) { printf( " %s ",*(dbResult + i*nColumn +j)); } printf("\n"); } sqlite3_free_table(dbResult); //关闭数据库 sqlite3_close( db ); return 0; }
实验结果:
[root@localhost sqlite3]# ./exec This is 2 column column name is :ID --->column value is :1 column name is :name --->column value is : on foot ------------------ This is 2 column column name is :ID --->column value is :2 column name is :name --->column value is : by bike ------------------ This is 2 column column name is :ID --->column value is :3 column name is :name --->column value is : by car ------------------ This is 2 column column name is :ID --->column value is :2 column name is :name --->column value is : by bike ------------------ The value of ID=2 is by bike ID name 1 on foot 2 by bike
SQLITE_API int sqlite3_exec( sqlite3 *db, /* The database on which the SQL executes */ const char *zSql, /* The SQL to be executed */ sqlite3_callback xCallback, /* Invoke this callback routine */ void *pArg, /* First argument to xCallback() */ char **pzErrMsg /* Write error messages here */ ){ int rc = SQLITE_OK; const char *zLeftover; sqlite3_stmt *pStmt = 0; char **azCols = 0; int nRetry = 0; int nCallback; /* 查询语句是空的 */ if( zSql==0 ) zSql = ""; /* 线程安全:加锁保护 */ sqlite3_mutex_enter(db->mutex); /* 设置错误为OK */ sqlite3Error(db, SQLITE_OK, 0); while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){ /*当返回SQLITE_SCHEMA时需要重新prepare,重新操作数据库;并且是仅重复1次,而且查询语句是存在的 */ /* 在 SQLite 版本3中,当一个预处理 SQL 语句不合法不能执行时就会返回一个 SQLITE_SCHEMA 错误。当这个错误发生时,该语句应当用 sqlite3_prepare() API函数重新编译。在 SQLite 版本3中,只有使用 sqlite3_prepare()/sqlite3_step()/sqlite3_finalize() API函数执行 SQL 才会发生这个错误,而使用 sqlite3_exec(). 则不会。这与版本2不同。 大部分发生这个错误的原因是当 SQL 预处理完时数据库已经改变了(可能是被另一个进程改变的)。还可能有如下原因: 对一个数据库进行DETACH操作 对一个数据库进行VACUUM操作 一个用户函数定义被删除或改变了。 一个排序定义被删除或改变了。 一个授权函数改变了。*/ int nCol; char **azVals = 0; pStmt = 0; /* prepare compile sql : 这里通过设置nByte可以加速操作,这里用了-1*/ rc = sqlite3_prepare(db, zSql, -1, &pStmt, &zLeftover); assert( rc==SQLITE_OK || pStmt==0 ); if( rc!=SQLITE_OK ){ continue; } if( !pStmt ){ /* 前导语句错误 . 这是在语句前有注释或者空格的条件下会发生 */ zSql = zLeftover; continue; } nCallback = 0; /* 查询列数 */ nCol = sqlite3_column_count(pStmt); while( 1 ){ int i; /* 查询数据 : 如果是插入数据等更新操作,会返回SQLITE_DONE ;如果是检索数据,会返回SQLITE_ROW */ /* 一次一行, 相当于移动光标 */ rc = sqlite3_step(pStmt); /* 如果存在回调函数,并且是检索类数据时调用回调 */ if( xCallback && (SQLITE_ROW==rc || (SQLITE_DONE==rc && !nCallback && db->flags&SQLITE_NullCallback)) ){ if( 0==nCallback ){ /* 首次执行call back ,分配列数据空间 */ if( azCols==0 ){ azCols = sqlite3DbMallocZero(db, 2*nCol*sizeof(const char*) + 1); if( azCols==0 ){ goto exec_out; } } /* 获取列名称 */ for(i=0; i<nCol; i++){ azCols[i] = (char *)sqlite3_column_name(pStmt, i); /* sqlite3VdbeSetColName() installs column names as UTF8 ** strings so there is no way for sqlite3_column_name() to fail. */ assert( azCols[i]!=0 ); } /* 回调变为1,改变了状态,下次不再调用 ,及仅需要执行一次列的信息提取工作*/ nCallback++; } if( rc==SQLITE_ROW ){ /* 计算查询的列数据:这里使用了c新标准--执行期间申请数组 */ azVals = &azCols[nCol]; for(i=0; i<nCol; i++){ azVals[i] = (char *)sqlite3_column_text(pStmt, i); if( !azVals[i] && sqlite3_column_type(pStmt, i)!=SQLITE_NULL ){ db->mallocFailed = 1; goto exec_out; } } } /* 执行回调函数:注意回调函数在正确的条件下要返回0,发生错误返回非0值;我一般错误条件下都是返回-1 */ if( xCallback(pArg, nCol, azVals, azCols) ){ rc = SQLITE_ABORT; sqlite3_finalize(pStmt); pStmt = 0; sqlite3Error(db, SQLITE_ABORT, 0); goto exec_out; } } if( rc!=SQLITE_ROW ){ /* 查询后处理:删除prepare object */ rc = sqlite3_finalize(pStmt); pStmt = 0; if( rc!=SQLITE_SCHEMA ){ nRetry = 0; zSql = zLeftover; /* 去掉sql 空格前导字符 */ while( isspace((unsigned char)zSql[0]) ) zSql++; } break; } /* 执行下一次查询处理 */ } /* 释放列信息空间 */ sqlite3DbFree(db, azCols); azCols = 0; } exec_out: /* 回收清除工作 */ if( pStmt ) sqlite3_finalize(pStmt); sqlite3DbFree(db, azCols); rc = sqlite3ApiExit(db, rc); if( rc!=SQLITE_OK && rc==sqlite3_errcode(db) && pzErrMsg ){ int nErrMsg = 1 + sqlite3Strlen30(sqlite3_errmsg(db)); *pzErrMsg = sqlite3Malloc(nErrMsg); if( *pzErrMsg ){ memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg); } }else if( pzErrMsg ){ *pzErrMsg = 0; } assert( (rc&db->errMask)==rc ); sqlite3_mutex_leave(db->mutex); return rc; }
sqlite3 *db创建数据库类型的指针,通过sqlite3_open()函数使db指针指向该数据库。
注意:
1、 char **dbResult; 字符型的二重指针,将数据库里sqlite3_get_table()出来的数据以字符的方式给dbResult。
2、select * from age;查询student数据库里的age表全部内容。
3、my_age = atoi(dbResult[nColumn]);将查询出来给dbResult的数据(字符)通过aoti()转换成整型交给变量my_age供程序中直接应用。
重点:(假设age表里有n个字段)
1、通过select * from age;给dbResult的字符前n个(0,n)为字段名称(只有计算机认识),dbResult[n]以后分别代表字段的值(包括dbResult[n])。如图
* * * * .........* (dbResult[0]~[n-1]分别代表字段名)
dbResult[n] [n+1] [n+2] [n+3].....[n+n-1] (dbResult[n]~[n+n-1]分别代表第一条记录的值)
dbResult[2n] [2n+1] [2n+2] [2n+3]....[2n+n-1](dbResult[2n]~[2n+n-1]分别代表第二条记录的值)
dbResult[3n] [3n+1] [3n+2] 32n+3]....[3n+n-1](dbResult[3n]~[3n+n-1]分别代表第三条记录的值)
注:sqlite3_get_table()之后便将以上的n(字段数:简称列)给了nColumn这个变量,可直接应用。nRow变量代表共有多少条记录,可直接应用。
2、通过select * from age where id=0;如果查到0条记录的话nRow等于0,查到1条记录的话nRow等于1,假设查到1条数据,举例:
* * * * .........* (dbResult[0]~[n-1]分别代表字段名)
dbResult[n] [n+1] [n+2] [n+3].....[n+n-1] (dbResult[n]~[n+n-1]分别代表第一条记录的值)
注:此时dbResult[]只有0~2n-1共2n个字符,此时如果对dbResult[2n]引用的话就会出错。查询两条语句的话以此类推。
* * * * .........* (dbResult[0]~[n-1]分别代表字段名)
dbResult[n] [n+1] [n+2] [n+3].....[n+n-1] (dbResult[n]~[n+n-1]分别代表第一条记录的值)
dbResult[2n] [2n+1] [2n+2] [2n+3]....[2n+n-1](dbResult[2n]~[2n+n-1]分别代表第二条记录的值)
注:此时dbResult[]只有0~3n-1可引用。