什么是SQLite?
SQLite是一个轻量级的数据库,它包含在一个相对小的C库中。它的设计目标是嵌入式的,而且目前已经在很多嵌入式产品中使用了它,它占用资源非常的低,在嵌入式设备中,可能只需要几百K的内存就够了。它能够支持Windows/Linux/Unix等等主流的操作系统,同时能够跟很多程序语言相结合。SQLite直接访问其存储文件。
SQLite的优点
- 不需要一个单独的服务器进程或操作的系统(无服务器的)。
- SQLite不需要配置,这意味着不需要安装或管理。
- 一个完整的SQLite数据库是存储在一个单一的跨平台的磁盘文件。
- SQLite是非常小的,是轻量级的,完全配置时小于400KB,省略可选功能配置时小于250KB。
- SQLite是自给自足的,这意味着不需要任何外部的依赖。
- SQLite事务是完全兼容ACID的,允许从多个进程或线程安全访问。
- SQLite支持SQL92(SQL2)标准的大多数查询语言的功能。
- SQLite使用ANSI-C编写的,并提供了简单和易于使用的API。
- SQLite可在UNIX(Linux、Mac OS-X、Android、iOS)和Windows(Win32、WinCE、WinRT)中运行。
SQLite的下载
本篇博客使用的SQLite头文件
提取码:gxlt
本篇博客使用的SQLite动态库
提取码:t35y
注意:本篇博客是在Visual Studio 2013下使用SQLite的教程。
SQLite的配置
- 下载下来后,全部解压。
- 在D盘新建一个文件夹sqlite3。
- 打开刚才解压得到的两个文件夹,将sqlite-dll-win32-x86-3290000文件夹中的sqlite3.def和sqlite.dll拷贝到D盘的sqlite3文件夹中,将sqlite-amalgamation-3290000文件夹中的sqlite3.h文件拷贝到D盘的sqlite3文件夹中。
- 使用sqlite3.def生成sqlite3.lib文件。
lib /out:D:\sqlite3\sqlite3.lib /MACHINE:IX86 /DEF:D:\sqlite3\sqlite3.def
- 生成成功。
- 最后将sqlite3.lib、sqlite3.dll、sqlite3.h分别拷贝到软件安装目录的lib、bin、include文件夹下。
- 使用的SQLite的时候添加下面两条语句即可。
#include <sqlite3.h>
#pragma comment(lib, "sqlite3.lib")
SQLite测试是否配置成功
- 首先来创建一个工程,点击新建项目。
- 点击VC++,点击空项目,输入项目名称,点击确定。
- 右键源文件点击添加新建项。
- 选择C++文件,输入名称,点击添加。
- 输入以下代码进行测试。
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#pragma comment(lib,"sqlite3.lib")
int main(int argc, char* argv[])
{
sqlite3 *db;
char *zErrMsg = 0;
int rc;
rc = sqlite3_open("test.db", &db);
if (rc){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
else{
fprintf(stderr, "Opened database successfully\n");
}
sqlite3_close(db);
system("pause");
return 0;
}
- 输出为Opened database successfully表示SQLite配置成功。
SQLite的使用
继续使用前面测试建立的工程。
SQLite的C/C++接口介绍
接口 | 描述 |
sqlite3_open(const char* filename, sqlite3** ppDb); | 该例程打开一个指向SQLite数据库文件的连接,返回一个用于其他SQLite程序的数据库连接对象。如果 filename 参数是 NULL 或 ':memory:',那么 sqlite3_open() 将会在 RAM 中创建一个内存数据库,这只会在 session 的有效时间内持续。如果文件名 filename 不为 NULL,那么 sqlite3_open() 将使用这个参数值尝试打开数据库文件。如果该名称的文件不存在,sqlite3_open() 将创建一个新的命名为该名称的数据库文件并打开。 |
sqlite3_exec(sqlite3*, const char* sql, sqlite_callback, void* data, char** errmsg); | 该例程提供了一个执行 SQL 命令的快捷方式,SQL 命令由 sql 参数提供,可以由多个 SQL 命令组成。在这里,第一个参数 sqlite3 是打开的数据库对象,sqlite_callback 是一个回调,data 作为其第一个参数,errmsg 将被返回用来获取程序生成的任何错误。sqlite3_exec() 程序解析并执行由 sql 参数所给的每个命令,直到字符串结束或者遇到错误为止。 |
sqlite3_close(sqlite3*); | 该例程关闭之前调用 sqlite3_open() 打开的数据库连接。所有与连接相关的语句都应在连接关闭之前完成。如果还有查询没有完成,sqlite3_close() 将返回 SQLITE_BUSY 禁止关闭的错误消息。 |
Navicat_for_SQLite的安装
在介绍SQLite具体使用之前,我们先来安装一个软件,方便查看创建的数据库中的数据。
Navicat下载
提取码:r5k9
安装过程:
- 下载下来之后,进行解压。
- 进入解压得到的文件夹。
- 右键navicat,点击发送到,桌面快捷方式。
- 双击桌面图标打开软件,效果如下。
表的创建
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#pragma comment(lib,"sqlite3.lib")
int main()
{
sqlite3 *db;
char *zErrMsg = 0;
int ret;
// 连接数据库
ret = sqlite3_open("lab330.db", &db);
if (ret){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
else{
fprintf(stderr, "Opened database successfully\n");
}
// SQL语句,创建表
char* create_tb = "create table student ("\
"id int unsigned primary key,"\
"name varchar(20) not null,"\
"hometown varchar(128) not null"\
");";
// 创建表
ret = sqlite3_exec(db, create_tb, nullptr, nullptr, &zErrMsg);
if (ret != SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else{
fprintf(stdout, "Table created successfully\n");
}
// 关闭数据库
sqlite3_close(db);
system("pause");
return 0;
}
首先,我们打开项目所在路径,可以看到数据库创建完成。
我们使用Navicat来看一下数据库中表是否创建成功。
- 打开Navicat,点击连接。
- 输入连接名(随便取),选择现有的数据库文件,选择数据库文件(刚才新建的数据库文件),点击连接测试。
- 点击连接测试后,弹出连接成功,点击确定即可,然后再点击下面的缺点。
- 连接上数据库,效果如下。
- 点击数据库,看一下其中的数据。
insert操作
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#pragma comment(lib,"sqlite3.lib")
int main()
{
sqlite3 *db;
char *zErrMsg = 0;
int ret;
// 连接数据库
ret = sqlite3_open("lab330.db", &db);
if (ret){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
else{
fprintf(stderr, "Opened database successfully\n");
}
// SQL语句,创建表
char* create_tb = "create table student ("\
"id int unsigned primary key,"\
"name varchar(20) not null,"\
"hometown varchar(128) not null"\
");";
// 创建表
ret = sqlite3_exec(db, create_tb, nullptr, nullptr, &zErrMsg);
if (ret != SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else{
fprintf(stdout, "Table created successfully\n");
}
// SQL语句,插入表
char* insert_tab = "insert into student values"\
"(1, 'Fan', 'ShanDong'),"\
"(2, 'Lin', 'HeBei'),"\
"(3, 'Niu', 'JiangSu');";
// 插入数据
ret = sqlite3_exec(db, insert_tab, nullptr, nullptr, &zErrMsg);
if (ret != SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else{
fprintf(stdout, "Table created successfully\n");
}
// 关闭数据库
sqlite3_close(db);
system("pause");
return 0;
}
使用Navicat查看表中插入的数据。
update操作
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#pragma comment(lib,"sqlite3.lib")
int main()
{
sqlite3 *db;
char *zErrMsg = 0;
int ret;
// 连接数据库
ret = sqlite3_open("lab330.db", &db);
if (ret){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
else{
fprintf(stderr, "Opened database successfully\n");
}
// SQL语句,创建表
char* create_tb = "create table student ("\
"id int unsigned primary key,"\
"name varchar(20) not null,"\
"hometown varchar(128) not null"\
");";
// 创建表
ret = sqlite3_exec(db, create_tb, nullptr, nullptr, &zErrMsg);
if (ret != SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else{
fprintf(stdout, "Table created successfully\n");
}
// SQL语句,插入表
char* insert_tab = "insert into student values"\
"(1, 'Fan', 'ShanDong'),"\
"(2, 'Lin', 'HeBei'),"\
"(3, 'Niu', 'JiangSu');";
// 插入数据
ret = sqlite3_exec(db, insert_tab, nullptr, nullptr, &zErrMsg);
if (ret != SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else{
fprintf(stdout, "Table created successfully\n");
}
// SQL语句,更新表
char* update_tab = "update student set name = 'F' where name = 'Fan';"\
"update student set name = 'L' where name = 'Lin';"\
"update student set name = 'N' where name = 'Niu';";
// 更新数据
ret = sqlite3_exec(db, update_tab, nullptr, nullptr, &zErrMsg);
if (ret != SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else{
fprintf(stdout, "Table created successfully\n");
}
// 关闭数据库
sqlite3_close(db);
system("pause");
return 0;
}
使用Navicat查看表中的数据。
delete操作
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#pragma comment(lib,"sqlite3.lib")
int main()
{
sqlite3 *db;
char *zErrMsg = 0;
int ret;
// 连接数据库
ret = sqlite3_open("lab330.db", &db);
if (ret){
fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
exit(0);
}
else{
fprintf(stderr, "Opened database successfully\n");
}
// SQL语句,创建表
char* create_tb = "create table student ("\
"id int unsigned primary key,"\
"name varchar(20) not null,"\
"hometown varchar(128) not null"\
");";
// 创建表
ret = sqlite3_exec(db, create_tb, nullptr, nullptr, &zErrMsg);
if (ret != SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else{
fprintf(stdout, "Table created successfully\n");
}
// SQL语句,插入表
char* insert_tab = "insert into student values"\
"(1, 'Fan', 'ShanDong'),"\
"(2, 'Lin', 'HeBei'),"\
"(3, 'Niu', 'JiangSu');";
// 插入数据
ret = sqlite3_exec(db, insert_tab, nullptr, nullptr, &zErrMsg);
if (ret != SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else{
fprintf(stdout, "Table created successfully\n");
}
// SQL语句,更新表
char* update_tab = "update student set name = 'F' where name = 'Fan';"\
"update student set name = 'L' where name = 'Lin';"\
"update student set name = 'N' where name = 'Niu';";
// 更新数据
ret = sqlite3_exec(db, update_tab, nullptr, nullptr, &zErrMsg);
if (ret != SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else{
fprintf(stdout, "Table created successfully\n");
}
// SQL语句,删除数据
char* delete_tab = "delete from student where name = 'L';"\
"delete from student where name = 'N';";
// 删除数据
ret = sqlite3_exec(db, delete_tab, nullptr, nullptr, &zErrMsg);
if (ret != SQLITE_OK){
fprintf(stderr, "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}
else{
fprintf(stdout, "Table created successfully\n");
}
// 关闭数据库
sqlite3_close(db);
system("pause");
return 0;
}
使用Navicat查看表中数据。
select操作
使用回调函数
首先让我们来了解一下回调函数,这个回调函数提供了一个从select语句获得结果的方式。声明如下。
typedef int(*sqlite3_callback)(
void*, /* Data provided in the 4th argument of sqlite3_exec() */
int, /* The number of columvs in row */
char**, /* An array of strings representing fields in the row */
char** /* An array of strings representing column names */
);
如果上面的回调函数在sqlite_exec()程序中作为第三个参数,那么SQLite将为SQL参数内执行的每个select语句中处理的每个记录调用这个回调函数。
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#pragma comment(lib,"sqlite3.lib")
static int callback(void *data, int argc, char **argv, char **azColName){
int i;
fprintf(stderr, "%s: \n", (const char*)data);
for (i = 0; i < argc; i++){
printf("%s = %s\t\t", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main()
{
sqlite3 *db;
char *zErrMsg = 0;
const char* data = "Callback function called";
// 连接数据库
sqlite3_open("lab330.db", &db);
// SQL语句,创建表
char* create_tb = "create table student ("\
"id int unsigned primary key,"\
"name varchar(20) not null,"\
"hometown varchar(128) not null"\
");";
// 创建表
sqlite3_exec(db, create_tb, nullptr, nullptr, &zErrMsg);
// SQL语句,插入表
char* insert_tab = "insert into student values"\
"(1, 'Fan', 'ShanDong'),"\
"(2, 'Lin', 'HeBei'),"\
"(3, 'Niu', 'JiangSu');";
// 插入数据
sqlite3_exec(db, insert_tab, nullptr, nullptr, &zErrMsg);
// SQL语句,更新表
char* update_tab = "update student set name = 'F' where name = 'Fan';"\
"update student set name = 'L' where name = 'Lin';"\
"update student set name = 'N' where name = 'Niu';";
// 更新数据
sqlite3_exec(db, update_tab, nullptr, nullptr, &zErrMsg);
// SQL语句,删除数据
char* delete_tab = "delete from student where name = 'L';"\
"delete from student where name = 'N';";
// 删除数据
sqlite3_exec(db, delete_tab, nullptr, nullptr, &zErrMsg);
// SQL语句,查询
char* select_tab = "select * from student;";
// 查询
sqlite3_exec(db, select_tab, callback, (void*)data, &zErrMsg);
// 关闭数据库
sqlite3_close(db);
system("pause");
return 0;
}
不使用回调函数
由于查询是一个比较频繁的操作,所以SQLite提供了对表进行查询的接口。
int sqlite3_get_table(
sqlite3 *db, /* An open database */
const char *zSql, /* SQL to be evaluated */
char ***pazResult, /* Results of the query */
int *pnRow, /* Number of result rows written here */
int *pnColumn, /* Number of result columns written here */
char **pzErrmsg /* Error msg written here */
);
void sqlite3_free_table(char **result);
上面可以看到,除了查表的接口之外,还有一个释放表的操作,这是为什么呢?
这需要我们去看一下sqlite3_get_table的参数列表。第一个参数是打开的数据库的句柄。第二个参数是SQL语句。后面四个参数都是输出型参数,也就是,我们传入一个指针,函数内部完成对它们的赋值。
其中pnROW,pnColumn参数是我们外部开辟好空间,然后传入它们的地址。但是pazResult和pzErrmsg参数是我们只是传入一个指针变量(空指针)的地址,空间的申请由函数内部动态分配,所以pazResult的空间通过调用sqlite3_free_table接口来释放。pzErrmsg的空间通过调用sqlite3_free接口来释放。
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
#pragma comment(lib,"sqlite3.lib")
int main()
{
sqlite3 *db;
char *zErrMsg = 0;
const char* data = "Callback function called";
// 连接数据库
sqlite3_open("lab330.db", &db);
// SQL语句,创建表
char* create_tb = "create table student ("\
"id int unsigned primary key,"\
"name varchar(20) not null,"\
"hometown varchar(128) not null"\
");";
// 创建表
sqlite3_exec(db, create_tb, nullptr, nullptr, &zErrMsg);
// SQL语句,插入表
char* insert_tab = "insert into student values"\
"(1, 'Fan', 'ShanDong'),"\
"(2, 'Lin', 'HeBei'),"\
"(3, 'Niu', 'JiangSu');";
// 插入数据
sqlite3_exec(db, insert_tab, nullptr, nullptr, &zErrMsg);
// SQL语句,更新表
char* update_tab = "update student set name = 'F' where name = 'Fan';"\
"update student set name = 'L' where name = 'Lin';"\
"update student set name = 'N' where name = 'Niu';";
// 更新数据
sqlite3_exec(db, update_tab, nullptr, nullptr, &zErrMsg);
// SQL语句,删除数据
char* delete_tab = "delete from student where name = 'L';"\
"delete from student where name = 'N';";
// 删除数据
sqlite3_exec(db, delete_tab, nullptr, nullptr, &zErrMsg);
// SQL语句,查询
char* select_tab = "select * from student;";
char** result = nullptr;
int row, col;
sqlite3_get_table(db, select_tab, &result, &row, &col, nullptr);
// 结果打印
for (int i = 0; i <= row; ++i){
printf("%s\t", result[i * col + 0]);
printf("%s\t", result[i * col + 1]);
printf("%s\n", result[i * col + 2]);
}
// 空间释放
sqlite3_free_table(result);
// 关闭数据库
sqlite3_close(db);
system("pause");
return 0;
}
看到上面的代码,可能会有疑问,我们使用result接收到的不应该是一个二维数组吗,尤其是我们还传入了row和col来获取它的行数和列数?为什么使用一维数组的方式打印。
其实,result获取的是一个一维数组。从参数上可以看出端倪。首先来看char*参数,由于它是一个输出型参数,所以可以去掉一个*,变为char**,由于数组中存的是字符串,所以数组中元素类型为char*,所以可以再去掉一个*,就变成了一个*,所以它只能用来接收一维数组。
而且需要注意的是,我们接收到的row不代表行数,而是实际接收到行数减一的值,所以对表进行遍历时,需要卡范围为[0, row]。
还有接收到的表的内容包含表的字段所在的那一行**,如果不想要字段行,可以将row的范围卡在[1, row]。