文章

sqlite指南

sqlite指南

SQLite 是一个 C 语言库,它实现了一个小型、快速、自包含、高可靠性、功能齐全的 SQL 数据库引擎。

sqlite

1 说明

SQLite 是一个 C 语言库,它实现了一个小型、快速、自包含、高可靠性、功能齐全的 SQL 数据库引擎。SQLite 是世界上使用最广泛的数据库引擎。

2 编译

VS2017 + sqlite-amalgamation-3390200 + sqlite-dll-win64-x64-3390200

2.1 下载

官方网址下载。

image-20220824000010556

其中 sqlite-amalgamation 包含所需的 sqlite3.hsqlite3.hsqlite3.hsqlite-dll-win64-x64 包含所需的 sqlite3.def。需要上述四个文件

2.2 创建工程

2.2.1 创建一个空项目

并导入上述四个文件。我选择的是 x64 Debug 版。

修改模块定义文件 sqlite3.def 在最后追加 sqlite3_unlock_notify(不知道为什么。。。)

image-20220824000623440

2.2.2 增加预处理定义

1
2
3
4
5
_USRDLL
SQLITE_ENABLE_RTREE
SQLITE_ENABLE_COLUMN_METADATA
SQLITE_ENABLE_FTS5
SQLITE_ENABLE_UNLOCK_NOTIFY

image-20220824001653164

2.2.3 添加模板定义文件 sqlite3.def

image-20220824001142622

2.2.4 生成解决方案

在项目路径的 x64/Debug 文件下,即可找到 .lib 文件。(同理可以配置 Release 版)

3 API

3.1 基础接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/**
 * @brief 根据文件名打开数据库对象
 *      1. 如果 filename 参数是 NULL 或 :memory:,那么 sqlite3_open() 将会在内存中创建一个内存数据库,
 *      这只会在 session 的有效时间内持续。
 *      2. 如果该名称的文件不存在,sqlite3_open() 将创建一个新的命名为该名称的数据库文件并打开。
 * 
 * @param filename  [in]    数据库文件名
 * @param ppDb      [out]   数据库对象
 */
SQLITE_API int sqlite3_open(
  const char *filename,   /* Database filename (UTF-8) */
  sqlite3 **ppDb          /* OUT: SQLite db handle */
);

/**
 * @brief 执行 SQL 语句的快捷函数
 * 
 * @param ppDb      [in]    数据库对象
 * @param sql       [in]    SQL 执行语句
 * @param callback  [in]    回调函数,第一个参数传入,第二个参数一次查询结果列数,第三个参数一次查询结果数据,第四个参数一次查询结果列表头
 * @param arg       [in]    回调函数的第一个参数
 * @param errmsg    [out]   错误信息
 */
SQLITE_API int sqlite3_exec(
  sqlite3*,                                  /* An open database */
  const char *sql,                           /* SQL to be evaluated */
  int (*callback)(void*,int,char**,char**),  /* Callback function */
  void *,                                    /* 1st argument to callback */
  char **errmsg                              /* Error msg written here */
);

/**
 * @brief 关闭打开的数据库
 *      1. 如果还有 SQL 语句没有执行完毕,sqlite3_close() 将返回 SQLITE_BUSY 禁止关闭的错误消息。
 * 
 * @param ppDb      [in]    数据库对象
 */
SQLITE_API int sqlite3_close(sqlite3*);

3.2 进阶接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/**
 * @brief 构建 SQL 语句
 *      1. 将要执行 SQL 语句编译成字节码,有占位符的 SQL 语句
 * 
 * @param db        [in]    数据库对象
 * @param zSql      [in]    需要构建的 SQL 语句,含有占位符 (?)
 * @param nByte     [in]    如果为负,则会读取到第一个终止符 `\0`;如果为正,则会读取整个 zSql;如果为0,则不会构建 zSql
 * @param ppStmt    [out]   数据库详细状态
 * @param pzTail    [out]   如果 pzTail 不为 NULL,则 *pzTail 指向 zSql 中第一个 SQL 语句末尾之后的第一个字节
 */
SQLITE_API int sqlite3_prepare(
  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 */
);

/**
 * @brief 构建 SQL 语句,二进制
 *      1. 给占位符设置数据
 * 
 * @param pStmt     [in]    数据库详细状态
 * @param index     [in]    要设置的 SQL 参数的索引(最左边的 SQL 参数的索引为 1)
 * @param data      [in]    传入数据
 * @param n         [in]    传入数据字节数
 * @param void      [in]    控制或指示第三个参数引用的对象的生命周期;(1)析构函数(2)SQLITE_STATIC(3)SQLITE_TRANSIENT
 */
SQLITE_API int sqlite3_bind_blob(
    sqlite3_stmt*, 
    int, 
    const void*, 
    int n, 
    void(*)(void*)
);

/**
 * @brief 执行 SQL 语句
 * 
 * @param pStmt     [in]    数据库详细状态
 */
SQLITE_API int sqlite3_step(sqlite3_stmt*);

/**
 * @brief 获取执行 SQL 语句第 iCol 列数据,二进制
 * 
 * @param pStmt     [in]    数据库详细状态
 * @param iCol      [in]    列数
 */
SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);

/**
 * @brief 重置 pStmt
 * 
 * @param pStmt     [in]    数据库详细状态
 */
SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt);

/**
 * @brief 删除 pStmt
 * 
 * @param pStmt     [in]    数据库详细状态
 */
SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt);

所有 SQL 都必须先转换为准备好的语句,然后才能运行。

准备好的语句对象的生命周期通常是这样的:

  1. 使用 sqlite3_prepare_v2() 创建准备好的语句对象。
  2. 使用 sqlite3_bind_*() 接口将值绑定到参数。
  3. 通过调用 sqlite3_step() 一次或多次运行 SQL
  4. 使用 sqlite3_reset() 重置准备好的语句,然后返回第 2 步。执行此操作零次或多次。
  5. 使用 sqlite3_finalize() 销毁对象。

4 数据类型

存储类描述
NULL值是一个 NULL 值。
INTEGER值是一个带符号的整数,根据值的大小存储在 1、2、3、4、6 或 8 字节中。
REAL值是一个浮点值,存储为 8 字节的 IEEE 浮点数字。
TEXT值是一个文本字符串,使用数据库编码(UTF-8、UTF-16BE 或 UTF-16LE)存储。
BLOB值是一个 blob 数据,完全根据它的输入存储。

5 使用

5.1 核心文件

1
2
3
sqlite3.h
sqlite3.c
sqlite3ext.h

5.3 代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#ifdef _WIN32  
#pragma execution_character_set("utf-8")  
#endif

#include <iostream>
#include <sqlite3.h>

int callback(void* NotUsed, int argc, char** argv, char** azColName)
{
	for (int i = 0; i < argc; i++)
	{
		printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
	}
	printf("\n");
	return 0;
}

void demo_insert()
{
	sqlite3* db = NULL; // 数据库
	char*    zErrMsg = NULL; // 错误信息
	char*    sql = NULL; // SQL 语句

	// 1 打开或创建数据库文件
	if (SQLITE_OK != sqlite3_open("test.db", &db))
	{
		fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
		exit(-1);
	}
	else
	{
		fprintf(stdout, "Open database successfully\n");
	}

	// 2 创建表
	sql = (char*)"CREATE TABLE t_company("
		"id       INTEGER     NOT NULL,"
		"name     TEXT        NOT NULL,"
		"age      INTEGER     NOT NULL,"
		"address  TEXT        NOT NULL,"
		"salary   REAL        NOT NULL);";

	if (SQLITE_OK != sqlite3_exec(db, sql, NULL, NULL, &zErrMsg))
	{
		fprintf(stderr, "SQL error: %s\n", zErrMsg);
		sqlite3_free(zErrMsg);
	}
	else
	{
		fprintf(stdout, "Table created successfully\n");
	}

	// 3 插入数据
	sql = (char*)"INSERT INTO t_company (id,name,age,address,salary)"
		"VALUES (1, 'Paul', 32, 'California', 20000.00);"
		"INSERT INTO t_company (id,name,age,address,salary)"
		"VALUES (2, 'Allen', 25, 'Texas', 15000.00);"
		"INSERT INTO t_company (id,name,age,address,salary)"
		"VALUES (3, 'Teddy', 23, 'Norway', 20000.00);"
		"INSERT INTO t_company (id,name,age,address,salary)"
		"VALUES (4, 'Mark', 25, 'Rich-Mond ', 65000.00);";
	if (SQLITE_OK != sqlite3_exec(db, sql, NULL, NULL, &zErrMsg))
	{
		fprintf(stderr, "SQL error: %s\n", zErrMsg);
		sqlite3_free(zErrMsg);
	}
	else
	{
		fprintf(stdout, "Records created successfully\n");
	}

	// 4 关闭数据库
	sqlite3_close(db);
}

typedef struct People
{
	char name[20];
	int  age;
} People;

void demo_insert_struct()
{
	sqlite3*      db = NULL; // 数据库
	char*         zErrMsg = NULL; // 错误信息
	char*         sql = NULL; // SQL 语句
	sqlite3_stmt* pstmt = NULL; // DB 状态
	int ret = 0;

	// 1 打开或创建数据库文件
	if (SQLITE_OK != sqlite3_open("test.db", &db))
	{
		fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
		exit(-1);
	}

	// 2 创建表
	sql = (char*)"CREATE TABLE t_company("
		"id       INTEGER     NOT NULL    PRIMARY KEY,"
		"struct   BLOB        NOT NULL);";
	if (SQLITE_OK != sqlite3_exec(db, sql, NULL, NULL, &zErrMsg))
	{
		fprintf(stderr, "SQL error: %s\n", zErrMsg);
		sqlite3_free(zErrMsg);
	}

	// 3 插入
	People people = { "Marry", 18 };
	sql = (char*)"INSERT INTO t_company(struct) VALUES(?);";
	ret = sqlite3_prepare(db, sql, -1, &pstmt, NULL);
	ret = sqlite3_bind_blob(pstmt, 1, (char*)&people, sizeof(People), NULL);
	ret = sqlite3_step(pstmt);

	// 4 查询
	sql = (char*)"SELECT * FROM t_company;";
	ret = sqlite3_prepare(db, sql, -1, &pstmt, NULL);
	while (SQLITE_ROW == sqlite3_step(pstmt))
	{
		const void* blob = sqlite3_column_blob(pstmt, 1);
		People      people_db = *(People*)blob;
		std::cout << "name: " << people_db.name << std::endl;
		std::cout << "age : " << people_db.age << std::endl;
	}

	// 5 关闭数据库
	sqlite3_finalize(pstmt);    // 记得释放指针,不然一直插入内存会一直涨
	sqlite3_close(db);
}

void demo_select()
{
	// 1 打开或创建数据库文件
	sqlite3* db;
	if (SQLITE_OK != sqlite3_open("test.db", &db))
	{
		fprintf(stderr, "Can't open database: %s\n", sqlite3_errmsg(db));
		exit(-1);
	}
	else
	{
		fprintf(stdout, "Open database successfully\n");
	}

	// 2 执行数据库语句
	char*       zErrMsg = NULL;
	const char* sql = "SELECT * FROM t_company";

	if (SQLITE_OK != sqlite3_exec(db, sql, callback, 0, &zErrMsg))
	{
		fprintf(stderr, "SQL error: %s\n", zErrMsg);
		sqlite3_free(zErrMsg);
	}
	else
	{
		fprintf(stdout, "Operation done successfully\n");
	}

	// 3 关闭数据库
	sqlite3_close(db);
}

int main(int argc, char** argv)
{
	system("chcp 65001");

	demo_insert();
	demo_select();
	//demo_insert_struct();

	return 0;
}

参考

[1] Luego——sqlite 安装与编译

[2] SQLite C/C++ 接口简介

本文由作者按照 CC BY 4.0 进行授权