基于C API的SQLite3基本数据库操作

  最近闲来无事,就写写博文来打发时间吧。。。

  这里把近期几个项目中总结下来的SQLite数据库使用经验归纳一下,共享出来,一来便于自己查阅,二来也便于和朋友们一起互相学习。

  这里先简单介绍一下SQLite数据库,给未接触过的朋友扫扫盲:
  SQLite是一款开源嵌入式文件型数据库,这个主要是和其他一些C/S架构的关系型数据库比较而来的,比如MySQL等。

  说他是嵌入式,因为SQLite的所有功能全部包装在一个dll中,我们只需要使用其中的导出接口就可以操作SQLite数据库,这样使得数据存储功能能够很方便的集成进用户的程序中,并运行在客户程序的进程空间中

  说他是文件型,因为SQLite的数据库文件就是一个独立文件(SQLite本身不限制数据库文件的扩展名),再没有其他的了,数据,表结构,查询,视图等等都保存在这个数据库文件中,不会依赖任何数据库环境

SQLite的主要特点:

  1. 无需部署,0配置,无服务端
  2. 跨平台
  3. 数据文件管理方便
  4. 较完善的SQL92标准支持,SQLite基本实现了SQL92标准,其他的一些不兼容的地方,可以参看官方相关说明,链接:http://www.sqlite.org/omitted.html
  5. SQL语句执行速度快,具体的对比数据,网上的评测有很多,这里就不多说了
  6. 应用较广。最著名的集成应该数Android了吧,其他的PHP ,Python等都做了集成,所以还是很不错的
  7. 完美的Unicode编码支持。SQLite的接口中,凡是涉及字符串的都是用UTF8或UTF16编码交互,有的同时提供这两种编码的接口函数,所以多语种支持绝对不是问题(这也是我偏爱SQLite的很重要的原因,呵呵)。

  好了,下面说说基于C API的SQLite操作,这里谢绝讨论其他的一些包装库,这不是本文的范围,当然SQLite的接口包装库函数还是很多的,基本覆盖各种主流开发语言。如基于Object Pascal的sqlitesimpledelphi,想了解这个库的朋友可以查看我的其他博文:

Delphi 2010下使用sqlitesimpledelphi连接SQLite数据库及中文乱码问题的解决
sqlitesimpledelphi修正以支持Unicode,Delphi 2010 测试通过

SQLite的数据库操作其实和常规的数据库操作流程是一样的:

  1. 连接数据库。
  2. 构造SQL语句并执行
  3. 对于SELECT语句,可以获取查询结果
  4. 数据库使用完毕之后,关闭数据库

  这里说明一下,下面所介绍的函数不会涵盖所有的API函数,毕竟SQLite针对同一个功能点提供了不同的API函数,主要表现在参数和配置功能上,有需要深入了解的朋友可以参考官方的文档。

打开数据库:

API函数:

int sqlite3_open(
const char *filename,   /* 数据库文件路径(UTF-8编码) */
sqlite3 **ppDb          /* 输出: SQLite 数据库句柄 */
);
int sqlite3_open16(
const void *filename,   /* 数据库文件路径(UTF-16) */
sqlite3 **ppDb          /* 输出: SQLite 数据库句柄 */
);

如果调用成功会返回SQLITE_OK,否则返回错误码。

构造SQL语句

这里就不多说了,这和SQLite本身无关,可以根据需要使用适当的方法构造即可,注意传给SQLite函数的时候,字符串编码要记得转换为UTF8/UTF16

执行SQL语句。

在SQLite中执行SQL语句比较简单的方法是调用函数:

int sqlite3_exec(
sqlite3*,                                  /* 打开的数据库句柄 */
const char *sql,                           /* UTF8编码的SQL语句 */
int (*callback)(void*,int,char**,char**),  /* 回调函数,对于SELECT语句返回的结果处理在回调函数中进行 */
void *,                                    /* 传递给回调函数的参数 */
char **errmsg                              /* 相关错误信息 */
);

其实sqlite3_exec只是封装了sqlite3_prepare、sqite3_step(即SQL中的预编译技术)的,主要目的是为使用者提供方便,笔者个人觉得使用后者会更加好一点,这里先做一下总结:

int sqlite3_prepare(
sqlite3 *db,            /* 打开的数据库句柄 */
const char *zSql,       /* UTF8编码的SQL语句,可以参数化 */
int nByte,              /* SQL语句的字节长度,可以传递-1,即字符串以\0结尾 */
sqlite3_stmt **ppStmt,  /* 输出:预编译之后的SQL语句句柄 */
const char **pzTail     /* 输出: 指向zSql缓冲区中跳过有效SQL字符串的第一个字节 */
);

int sqlite3_prepare_v2(
sqlite3 *db,            /* 打开的数据库句柄 */
const char *zSql,       /* UTF8编码的SQL语句,可以参数化 */
int nByte,              /* SQL语句的字节长度,可以传递-1,即字符串以宽字符\0结尾 */
sqlite3_stmt **ppStmt,  /* 输出: 预编译之后的SQL语句句柄 */
const char **pzTail     /* 输出: 指向zSql缓冲区中跳过有效SQL字符串的第一个字节 */
);

int sqlite3_prepare16(
sqlite3 *db,            /* 打开的数据库句柄 */
const void *zSql,       /* UTF16编码的SQL语句,可以参数化 */
int nByte,              /* SQL语句的字节长度,可以传递-1,即字符串以宽字符\0结尾 */
sqlite3_stmt **ppStmt,  /* 输出: 预编译之后的SQL语句句柄 */
const void **pzTail     /* 输出: 指向zSql缓冲区中跳过有效SQL字符串的第一个字节 */
);

int sqlite3_prepare16_v2(
sqlite3 *db,            /* 打开的数据库句柄 */
const void *zSql,       /* UTF16编码的SQL语句,可以参数化 */
int nByte,              /* SQL语句的字节长度,可以传递-1,即字符串以宽字符\0结尾 */
sqlite3_stmt **ppStmt,  /* 输出: 预编译之后的SQL语句句柄 */
const void **pzTail     /* 输出: 指向zSql缓冲区中跳过有效SQL字符串的第一个字节 */
);

其中带参数的SQL语句可以这样定义参数:
?
?NNN
:VVV
@VVV
$VVV
参数编号从1开始,例如:INSERT into db values(?1, ?2)

v2版本函数时SQLite根据需要添加的增强函数,新的程序推荐使用v2版本函数,只需与原来函数的区别,可以参考官方原文

int sqlite3_step(sqlite3_stmt*);
执行一次预编译SQL语句,在这之前,如果SQL是参数化的,可以调用sqlite3_bind来绑定数据,int、string、blob等等
如果执行成功会返回SQLITE_DONE,如果查询有结果会返回SQLITE_ROW,并可以通过API获取结果中的第一行数据,需要获取下一行数据可以再次调用sqlite3_step直到返回SQLITE_DONE表示后面没有数据了
如果需要重新对预编译的SQL绑定数据并执行,需要先reset一下,然后再调用step,即函数:
int sqlite3_reset(sqlite3_stmt *pStmt);

下面是绑定数据到预编译SQL语句的相关函数:
以下函数的第一个参数指代预编译的SQL句柄,第二个参数指代绑定的参数编号,对应于参数化的SQL语句中的参数编号:

int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void()(void));
该函数用于绑定二进制数据BLOB,其中最后一个参数是一个回调函数,当成功绑定数据后,会被调用,一般用于自动释放对应的缓冲区

int sqlite3_bind_double(sqlite3_stmt*, int, double);
该函数绑定double浮点数

int sqlite3_bind_int(sqlite3_stmt*, int, int);
该函数绑定int整数

int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
该函数用于绑定具有64位长度的整数,对应于C中的long long结构,由于一个int的范围可能无法满足超大数据量的要求,所以SQLite也支持64位整数,毕竟SQLite官方声称SQLite是支持最大2T的数据的

int sqlite3_bind_null(sqlite3_stmt*, int);
该函数绑定一个空数据到指定列

int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void()(void));
该函数绑定一段字符串,源字符串是UTF8编码的

int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void()(void));
该函数绑定一段字符串,源字符串是UTF16编码的

int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
该函数绑定以SQLite结构sqlite3_value存储的通用数据,其中sqlite3_value可以是上述的所有类型,此函数不太常用

int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);
该函数绑定指定大小的全零BLOB数据

获取SQL查询结果

对于SELECT语句,还需要能够获取结果。上面也提到调用sqlite3_step之后,对于有结果的查询会返回第一行结果,这时可以通过API函数获取当前行的指定字段结果:

const void sqlite3_column_blob(sqlite3_stmt, int iCol);
该函数以BLOB数据格式获取对应列的数据,BOLB长度使用sqlite3_column_bytes获取

int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
该函数可以用于返回BLOB和字符串的字节长度。对于BLOB,两个函数效果是一样的,但是对于字符串sqlite3_column_bytes返回的是UTF8编码的字符串长度,而sqlite3_column_bytes16返回的是UTF16编码的字符串长度,其间会做必要的字符串格式转换

double sqlite3_column_double(sqlite3_stmt*, int iCol);
该函数返回double数据列

int sqlite3_column_int(sqlite3_stmt*, int iCol);
该函数返回int数据列

sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
该函数返回64位整数,即long long数据

const unsigned char sqlite3_column_text(sqlite3_stmt, int iCol);
const void sqlite3_column_text16(sqlite3_stmt, int iCol);
该函数返回字符串,其中sqlite3_column_text输出的字符串使用UTF8编码

sqlite3_column_text16使用UTF16编码
int sqlite3_column_type(sqlite3_stmt*, int iCol);
该函数返回对应列的数据类型

sqlite3_value sqlite3_column_value(sqlite3_stmt, int iCol);
该函数以sqlite3_value结构体返回数据

上面是根据列ID来获取对应的列数据的,如果想通过列名称获取列数据,则需要将列名称转换为对应的列ID,可以使用下面的函数:
const char sqlite3_column_name(sqlite3_stmt, int N);
const void sqlite3_column_name16(sqlite3_stmt, int N);
该函数返回对应列的名称

关闭数据库

int sqlite3_close(sqlite3 * db);
使用该函数可以关闭数据库

好了,基本的调用流程就这么些了,看完这篇文章,应该能够完成基本的数据读写操作了,至于其他的高级操作,有兴趣的朋友可以登录SQLite官方网站查看,这里就不在本文多说了。

OK,打完收功,欢迎大家多多讨论指正^^^_^

标签: none

添加新评论