sqlite3的简单使用

时间:2021-09-30 05:28:29

实验代码

#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可引用。