SQLite是一个跨平台的轻量级数据库,支持C/C 开发,可用于嵌入式中,关于C/C 使用SQLite的简单实例,之前这篇文章,已经介绍过一种简单的使用方式。本篇来介绍另一种更加高效的调用方式。
1.1 普通方式
之前的文章介绍过sqlite3的C语言API函数基础操作,通过sqlite3_exec函数即可执行sql语句函数,该函数指定一个 sql语句字符串和对应的回调函数。
当执行sqlite3_exec时,其内部的执行可分为3步:
- 解析sql语句字符串
- 编译sql语句
- 执行sql语句
可以看到,sqlite3_exec一个函数就实现了这么多功能,这是它的优点——使用方便,但同时也是它的缺点——效率低,因为解析和编译都是比较耗时的。
关于sqlite3_exec的使用示例可参考之前的文章:玩转SQLite6:使用C语言来读写数据库
1.2 高效方式
为此解决sqlite3_exec函数执行效率低的问题,就出现了其它更加高效的解决方式:将sqlite3_exec的功能进行分解,由多个函数共同完成。这就是本篇要介绍的:
- sqlite3_prepare_v2()函数:实现对sql语句(模板)的解析和编译,生成了可以被执行的 sql语句实例
- sqlite3_stmt()数据结构:可以理解为一种“准备语句对象”,它可以结合变量使用,进而实现相同操作的循环
- sqlite3_bind_*() 函数:用于绑定赋值变量
- sqlite3_step() 函数:用于执行sql语句
相比较使用sqlite3_exec函数,现在这种方式,sql语句的解析和编译只执行了一次,而sqlite3_step执行多次,整体的效率势必大大提升。
2 函数介绍
2.1 sqlite3错误码
在介绍各个函数之前,先来看一个这些函的错误码有哪些
代码语言:javascript复制#define SQLITE_OK 0 /* 成功 */
/* 错误码 */
#define SQLITE_ERROR 1 /* sql错误或丢失的数据库,SQL error or missing database */
#define SQLITE_INTERNAL 2 /* sqlite内部逻辑错误,Internal logic error in SQLite */
#define SQLITE_PERM 3 /* 拒绝访问,Access permission denied */
#define SQLITE_ABORT 4 /* 回调函数请求取消操作,Callback routine requested an abort */
#define SQLITE_BUSY 5 /* 数据库文件被锁定,The database file is locked */
#define SQLITE_LOCKED 6 /* 数据库的一个表被锁定,A table in the database is locked */
#define SQLITE_NOMEM 7 /* 某次malloc函数调用失败,A malloc() failed */
#define SQLITE_READONLY 8 /* 尝试写入一个只读数据库,Attempt to write a readonly database */
#define SQLITE_INTERRUPT 9 /* 操作sqlite3_interrupt函数被中断,Operation terminated by sqlite3_interrupt()*/
#define SQLITE_IOERR 10 /* 发生磁盘I/O错误,Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT 11 /* 数据库磁盘映像不正确,The database disk image is malformed */
#define SQLITE_NOTFOUND 12 /* 找不到表或记录,NOT USED. Table or record not found */
#define SQLITE_FULL 13 /* 数据库满而插入失败,Insertion failed because database is full */
#define SQLITE_CANTOPEN 14 /* 无法打开数据库,Unable to open the database file */
#define SQLITE_PROTOCOL 15 /* 数据库锁定协议错误,NOT USED. Database lock protocol error */
#define SQLITE_EMPTY 16 /* 数据库为空,Database is empty */
#define SQLITE_SCHEMA 17 /* 数据库结构发生改变,The database schema changed */
#define SQLITE_TOOBIG 18 /* 数据大小超限,String or BLOB exceeds size limit */
#define SQLITE_CONSTRAINT 19 /* 约束违反,Abort due to constraint violation */
#define SQLITE_MISMATCH 20 /* 数据类型不匹配,Data type mismatch */
#define SQLITE_MISUSE 21 /* 库使用不正确,Library used incorrectly */
#define SQLITE_NOLFS 22 /* 使用了操作系统不支持的功能,Uses OS features not supported on host */
#define SQLITE_AUTH 23 /* 授权失败,Authorization denied */
#define SQLITE_FORMAT 24 /* 附加数据库格式错误,Auxiliary database format error */
#define SQLITE_RANGE 25 /* sqlite3_bind的第2给参数超出范围,2nd parameter to sqlite3_bind out of range */
#define SQLITE_NOTADB 26 /* 不是数据库文件,File opened that is not a database file */
#define SQLITE_NOTICE 27 /* Notifications from sqlite3_log() */
#define SQLITE_WARNING 28 /* Warnings from sqlite3_log() */
#define SQLITE_ROW 100 /* sqlite3_step产生一个就绪行,sqlite3_step() has another row ready */
#define SQLITE_DONE 101 /* sqlite3_step执行完成,sqlite3_step() has finished executing */
2.2 sqlite3_prepare_v2
该函数实现对sql语句(模板)的解析和编译,生成了可以被执行的sql语句实例
代码语言:javascript复制int sqlite3_prepare_v2(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL statement, UTF-8 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);
参数:
- db:sqlite数据库
- zSql:要执行的sql语句(可以包含未赋值的变量)
- nByte:sql语句的(字符串的)长度
- ppStmt:解析编译出的sql语句实例
- pzTail:
返回值:见前面的sqlite3错误码
2.3 sqlite3_bind
该函数组用于绑定变量值到prepare语句中,也就是给 sqlite3_stmt变量赋值。前面的文章讲过,我们一定是先通过sqlite3_prepare_v2函数创建并初始化一个 sqlite3_stmt 变量语句,然后使用sqlite3_bind_xxx函数对 这个 sql语句变量进行绑定参数。
代码语言:javascript复制int sqlite3_bind_int(sqlite3_stmt*, int, int);
int sqlite3_bind_doubule(sqlite3_stmt*, int, double);
int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int, void(*)(void*));
参数:
- 形参1:sqlite3_stmt: prepare语句编译出的sql语句实例
- 形参2:sqlite3_stmt变量参数的序号索引值,规定最左侧的SQL参数的索引值为 1,也就是说参数索引值从1开始。
- 形参3:是要绑定给第2个形参指向的 变量参数的 实际值。第2个形参可以指向不同的索引值。
- 形参4:对于有4个形参的函数,第4个形参一般是第3个形参的长度。
- 形参5:是用于BLOB和字符串绑定后的 析构函数,用于在sqlite处理完blob或字符串之后处理它,一般可以设置为NULL。
返回值:见前面的sqlite3错误码
代码示例:假设表的字段结构为:person(name,age,sex),数据库指针为 pdb。
代码语言:javascript复制 sqlite3_stmt *pstmt;
const char *sql = "INSERT INTO person(name, age, sex) VALUES(?,?,?);";
nRet = sqlite3_prepare_v2(pdb, sql, strlen(sql), &pstmt, &pzTail);
int i;
for(i = 0; i < 10; i ){
nCol = 1;
sqlite3_bind_text(pstmt, nCol , a[i].name, strlen(a[i].name), NULL);
sqlite3_bind_int(pstmt, nCol , a[i].age);
sqlite3_bind_text(pstmt, nCol , a[i].sex, strlen(a[i].name), NULL);
sqlite3_step(pstmt);
sqlite3_reset(pstmt);
}
sqlite3_finalize(pstmt);
2.4 sqlite3_step
代码语言:javascript复制int sqlite3_step(sqlite3_stmt *pStmt);
参数:
- pStmt:prepare语句编译出的sql语句实例
返回值:
这里再对几个常见的返回值进一步说明:
- SQLITE_DONE:意味着sql语句执行完成且成功。一旦执行成功后,sqlite3_step()就不应该被再次调用执行,除非我们使用sqlite3_reset()重置 sqlite3_stmt 数据。
- SQLITE_ROW:这个比较常用,当我们的sql语句是 读命令,比如"SELECT* FROM...",返回的数据一般很多,并且数据是按行返回的,且每次只返回一行,其返回值为 SQLITE_ROW,所以需要重复调用sqlite3_step函数,直到sqlite3_step返回 SQLITE_DONE.
- SQLITE_MISUSE:表示该函数实例被滥用,不合适,比如sqlite_stmt结构已经被销毁了。
2.5 sqlite3_reset
代码语言:javascript复制int sqlite3_reset(sqlite3_stmt *pStmt);
用于重置一个准备语句对象到它的初始状态,然后准备被重新执行。所有sql语句变量使用sqlite3_bind*绑定值,使用sqlite3_clear_bindings重设这些绑定。Sqlite3_reset接口重置准备语句到它代码开始的时候。sqlite3_reset并不改变在准备语句上的任何绑定值,那么这里猜测,可能是语句在被执行的过程中发生了其他的改变,然后这个语句将它重置到绑定值的时候的那个状态。
2.6 sqlite3_column
该函数实例用于 查询(query)结果的筛选,返回当前结果的某1列。
代码语言:javascript复制int sqlite3_column_int(sqlite3_stmt*, int iCol);
double sqlite3_column_double(sqlite3_stmt*, int iCol);
const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
参数:
- sqlite3_stmt*:prepare语句编译出的sql语句实例
- iCol: 要查询的"列"索引值。sqlite3规定最左侧的“列”索引值是 0,也就是“列”索引号从 0 开始。
返回:
- 根据函数类型,返回相应的数据,比如int型,double型(浮点数也是),text(字符串型)等。
3 总结
本篇主要介绍了sqlite的C语言操作的高效API函数,用于取代功能强大但效率较低的sqlite3_exec函数。本篇介绍到的几个API函数总结如下:
- sqlite3_prepare_v2() 创建sqlite3_stmt对象
- sqlite3_bind_*() 绑定参数值到sqlite3_stmt
- sqlite3_step() 运行sql语句,可以是一次,也可以是循环执行
- sqlite3_reset() 重置sqlite3_stmt对象
- sqlite3_finalize() 销毁sqlite3_stmt对象
下篇,将通过一个实际的例子,来体会这些函数的具体使用效果。