1. SQLite数据库入门指南
SQLite作为一款轻量级的关系型数据库管理系统,以其零配置、无服务器、单文件存储的特性,在嵌入式系统和移动应用中广受欢迎。与MySQL、Oracle等大型数据库不同,SQLite不需要单独的服务器进程,数据库直接存储在磁盘文件中,这使得它在资源受限的环境中表现出色。
我第一次接触SQLite是在开发一个嵌入式设备的数据采集系统时。当时需要在有限的硬件资源上实现数据存储功能,SQLite的轻量级特性完美解决了这个问题。从那以后,无论是移动应用开发、桌面软件还是小型Web应用,SQLite都成为了我的首选数据库解决方案。
2. SQLite环境搭建与配置
2.1 安装SQLite3
在Ubuntu/Debian系统上安装SQLite非常简单,只需执行以下命令:
sudo apt-get update sudo apt-get install sqlite3 libsqlite3-dev sqlitebrowser这三个包分别提供了:
- sqlite3:命令行交互工具
- libsqlite3-dev:C语言开发所需的头文件和静态库
- sqlitebrowser:图形化管理工具(可选)
安装完成后,可以通过sqlite3 --version命令验证安装是否成功。
2.2 基本命令行操作
启动SQLite命令行界面:
sqlite3 test.db这会创建一个名为test.db的数据库文件(如果不存在的话),并进入交互模式。在交互模式下,可以执行两种命令:
- 以点号(.)开头的SQLite特有命令
- 标准的SQL语句
常用管理命令:
.databases:显示当前连接的数据库.tables:列出所有表.schema 表名:显示表的创建语句.mode column:设置输出格式为列模式.headers on:显示列名.quit:退出交互模式
提示:SQLite的命令行工具虽然简单,但功能非常完整。熟练掌握这些命令可以大大提高工作效率,特别是在没有图形界面可用的环境下。
3. SQL基础与实战
3.1 数据表创建与管理
创建学生信息表的SQL示例:
CREATE TABLE students ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, gender TEXT CHECK(gender IN ('男', '女')), age INTEGER, score REAL, class_id INTEGER, FOREIGN KEY (class_id) REFERENCES classes(id) );关键点解析:
PRIMARY KEY:定义主键,AUTOINCREMENT确保主键自动增长NOT NULL:约束该列不能为空CHECK:添加数据验证规则FOREIGN KEY:定义外键关系
创建班级表的SQL:
CREATE TABLE classes ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, teacher TEXT );3.2 数据操作语言(DML)
插入数据
-- 插入完整记录 INSERT INTO students VALUES (NULL, '张三', '男', 18, 85.5, 1); -- 插入部分字段 INSERT INTO students (name, gender, age) VALUES ('李四', '女', 17); -- 批量插入 INSERT INTO students (name, gender, age, score, class_id) VALUES ('王五', '男', 18, 90.0, 1), ('赵六', '女', 17, 88.5, 2);更新数据
-- 更新单个记录 UPDATE students SET score = 92.0 WHERE name = '张三'; -- 批量更新 UPDATE students SET score = score + 5 WHERE class_id = 1; -- 使用表达式更新 UPDATE students SET age = age + 1 WHERE strftime('%Y-%m-%d', 'now') = strftime('%Y-%m-%d', birthday);删除数据
-- 删除特定记录 DELETE FROM students WHERE id = 3; -- 清空表(慎用!) DELETE FROM students;注意:SQLite中DELETE没有WHERE子句会删除整个表的数据,但表结构保留。与TRUNCATE TABLE不同,DELETE操作会记录日志,可以回滚。
3.3 数据查询进阶
基础查询
-- 查询所有字段 SELECT * FROM students; -- 查询特定字段 SELECT name, score FROM students; -- 使用别名 SELECT name AS 姓名, score AS 成绩 FROM students;条件查询
-- 比较运算符 SELECT * FROM students WHERE score >= 60; -- 逻辑运算符 SELECT * FROM students WHERE score >= 60 AND gender = '女'; -- IN操作符 SELECT * FROM students WHERE class_id IN (1, 3); -- BETWEEN SELECT * FROM students WHERE score BETWEEN 70 AND 90; -- LIKE模糊查询 SELECT * FROM students WHERE name LIKE '张%'; -- 日期查询 SELECT * FROM students WHERE birthday > '2005-01-01';排序与分页
-- 单列排序 SELECT * FROM students ORDER BY score DESC; -- 多列排序 SELECT * FROM students ORDER BY class_id ASC, score DESC; -- 分页查询 SELECT * FROM students LIMIT 10 OFFSET 20; -- 等价于 SELECT * FROM students LIMIT 20, 10;聚合函数
-- 基本聚合 SELECT COUNT(*) AS total, AVG(score) AS avg_score, MAX(score) AS max_score, MIN(score) AS min_score FROM students; -- 分组聚合 SELECT class_id, COUNT(*) AS student_count, AVG(score) AS avg_score FROM students GROUP BY class_id HAVING COUNT(*) > 5;4. 多表操作与复杂查询
4.1 连接查询
内连接(INNER JOIN)
SELECT s.name AS student_name, c.name AS class_name FROM students s INNER JOIN classes c ON s.class_id = c.id;左外连接(LEFT OUTER JOIN)
SELECT s.name AS student_name, c.name AS class_name FROM students s LEFT JOIN classes c ON s.class_id = c.id;交叉连接(CROSS JOIN)
-- 生成学生和班级的所有可能组合 SELECT s.name AS student_name, c.name AS class_name FROM students s CROSS JOIN classes c;4.2 子查询
WHERE子句中的子查询
-- 查询高于平均分的学生 SELECT * FROM students WHERE score > (SELECT AVG(score) FROM students);FROM子句中的子查询
-- 查询每个班级的平均分 SELECT c.name AS class_name, s.avg_score FROM classes c JOIN ( SELECT class_id, AVG(score) AS avg_score FROM students GROUP BY class_id ) s ON c.id = s.class_id;EXISTS子查询
-- 查询有学生的班级 SELECT * FROM classes c WHERE EXISTS ( SELECT 1 FROM students s WHERE s.class_id = c.id );4.3 视图与索引
创建视图
CREATE VIEW student_scores AS SELECT s.name AS student_name, c.name AS class_name, s.score FROM students s JOIN classes c ON s.class_id = c.id;使用视图
-- 查询视图 SELECT * FROM student_scores WHERE score > 80; -- 视图也可以参与连接 SELECT ss.student_name, ss.score, a.activity_name FROM student_scores ss JOIN activities a ON ss.student_id = a.student_id;创建索引
-- 单列索引 CREATE INDEX idx_students_name ON students(name); -- 复合索引 CREATE INDEX idx_students_class_score ON students(class_id, score); -- 唯一索引 CREATE UNIQUE INDEX idx_students_id_card ON students(id_card);经验分享:索引虽然能提高查询速度,但会降低插入和更新性能。建议只在经常用于查询条件的列上创建索引,并且避免在小表上创建不必要的索引。
5. SQLite C语言编程接口
5.1 基本API介绍
SQLite提供了丰富的C语言API,核心函数包括:
sqlite3_open()- 打开数据库连接sqlite3_exec()- 执行SQL语句sqlite3_prepare_v2()- 准备SQL语句sqlite3_step()- 执行预编译语句sqlite3_finalize()- 释放预编译语句sqlite3_close()- 关闭数据库连接
5.2 完整示例代码
#include <stdio.h> #include <sqlite3.h> int callback(void *data, int argc, char **argv, char **colName) { for(int i = 0; i < argc; i++) { printf("%s = %s\n", colName[i], argv[i] ? argv[i] : "NULL"); } printf("\n"); return 0; } int main() { sqlite3 *db; char *err_msg = NULL; // 打开数据库 int rc = sqlite3_open("test.db", &db); if(rc != SQLITE_OK) { fprintf(stderr, "无法打开数据库: %s\n", sqlite3_errmsg(db)); sqlite3_close(db); return 1; } // 创建表 const char *sql = "CREATE TABLE IF NOT EXISTS users(" "id INTEGER PRIMARY KEY AUTOINCREMENT," "name TEXT NOT NULL," "email TEXT UNIQUE NOT NULL);"; rc = sqlite3_exec(db, sql, 0, 0, &err_msg); if(rc != SQLITE_OK) { fprintf(stderr, "SQL错误: %s\n", err_msg); sqlite3_free(err_msg); sqlite3_close(db); return 1; } // 插入数据 sql = "INSERT INTO users (name, email) VALUES ('张三', 'zhangsan@example.com');" "INSERT INTO users (name, email) VALUES ('李四', 'lisi@example.com');"; rc = sqlite3_exec(db, sql, 0, 0, &err_msg); if(rc != SQLITE_OK) { fprintf(stderr, "SQL错误: %s\n", err_msg); sqlite3_free(err_msg); } // 查询数据 sql = "SELECT * FROM users;"; rc = sqlite3_exec(db, sql, callback, 0, &err_msg); if(rc != SQLITE_OK) { fprintf(stderr, "SQL错误: %s\n", err_msg); sqlite3_free(err_msg); } // 使用预编译语句 sqlite3_stmt *stmt; sql = "SELECT * FROM users WHERE name LIKE ?;"; rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); if(rc != SQLITE_OK) { fprintf(stderr, "准备语句失败: %s\n", sqlite3_errmsg(db)); } else { sqlite3_bind_text(stmt, 1, "%张%", -1, SQLITE_STATIC); while(sqlite3_step(stmt) == SQLITE_ROW) { printf("id = %d, name = %s, email = %s\n", sqlite3_column_int(stmt, 0), sqlite3_column_text(stmt, 1), sqlite3_column_text(stmt, 2)); } sqlite3_finalize(stmt); } // 关闭数据库 sqlite3_close(db); return 0; }5.3 预编译语句的使用
预编译语句(Prepared Statement)是SQLite中执行SQL的高效方式,特别适合需要多次执行的语句:
// 准备插入语句 const char *sql = "INSERT INTO users (name, email) VALUES (?, ?);"; sqlite3_stmt *stmt; rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); if(rc != SQLITE_OK) { // 错误处理 } // 绑定参数并执行 sqlite3_bind_text(stmt, 1, "王五", -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, "wangwu@example.com", -1, SQLITE_STATIC); rc = sqlite3_step(stmt); if(rc != SQLITE_DONE) { // 错误处理 } // 重置语句以便重用 sqlite3_reset(stmt); // 再次绑定和执行 sqlite3_bind_text(stmt, 1, "赵六", -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, "zhaoliu@example.com", -1, SQLITE_STATIC); rc = sqlite3_step(stmt); // 释放语句 sqlite3_finalize(stmt);5.4 事务处理
事务是数据库操作的重要特性,可以确保一组操作要么全部成功,要么全部失败:
// 开始事务 rc = sqlite3_exec(db, "BEGIN TRANSACTION;", 0, 0, &err_msg); if(rc != SQLITE_OK) { // 错误处理 } // 执行多个操作 rc = sqlite3_exec(db, "INSERT INTO users (name, email) VALUES ('钱七', 'qianqi@example.com');", 0, 0, &err_msg); if(rc == SQLITE_OK) { rc = sqlite3_exec(db, "UPDATE users SET email = 'new_email@example.com' WHERE name = '张三';", 0, 0, &err_msg); } // 根据结果提交或回滚 if(rc == SQLITE_OK) { rc = sqlite3_exec(db, "COMMIT;", 0, 0, &err_msg); } else { sqlite3_exec(db, "ROLLBACK;", 0, 0, NULL); fprintf(stderr, "事务失败: %s\n", err_msg); sqlite3_free(err_msg); }6. 性能优化与最佳实践
6.1 性能优化技巧
使用事务:将多个操作放在一个事务中可以显著提高性能。SQLite默认对每个语句都开启一个事务,频繁提交会影响性能。
合理使用索引:在经常查询的列上创建索引,但避免过度索引,因为索引会降低插入和更新速度。
预编译语句:对于重复执行的SQL语句,使用预编译语句可以减少解析开销。
批量操作:批量插入数据时,使用单个事务和预编译语句。
合理设置PRAGMA:
PRAGMA journal_mode = WAL; -- 使用WAL日志模式提高并发性 PRAGMA synchronous = NORMAL; -- 平衡安全性和性能 PRAGMA cache_size = -2000; -- 设置缓存大小为2MB
6.2 常见问题与解决方案
数据库被锁定:
- 原因:多个连接同时尝试写入
- 解决方案:使用WAL模式,减少事务持有时间,合理设计应用架构
性能突然下降:
- 可能原因:数据库需要VACUUM
- 解决方案:定期执行
VACUUM命令整理数据库文件
C语言接口内存泄漏:
- 预防措施:确保每个
sqlite3_prepare_v2()都有对应的sqlite3_finalize(),每个sqlite3_open()都有对应的sqlite3_close()
- 预防措施:确保每个
跨平台兼容性问题:
- 注意:不同平台的文件路径表示方法不同
- 解决方案:使用跨平台路径处理库或相对路径
6.3 安全注意事项
SQL注入防护:
- 永远不要拼接SQL语句
- 始终使用参数化查询(预编译语句)
敏感数据保护:
- 考虑使用SQLite的加密扩展
- 或在应用层加密敏感数据
备份策略:
- 定期备份数据库文件
- 使用
.dump命令导出SQL脚本
7. 实际项目案例
7.1 学生成绩管理系统
这个案例展示了如何使用SQLite和C语言构建一个完整的学生成绩管理系统,包括数据库设计、核心功能和实现代码。
数据库设计
-- 班级表 CREATE TABLE classes ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, teacher TEXT ); -- 学生表 CREATE TABLE students ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, gender TEXT CHECK(gender IN ('男', '女')), class_id INTEGER, FOREIGN KEY (class_id) REFERENCES classes(id) ); -- 课程表 CREATE TABLE courses ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, credit INTEGER ); -- 成绩表 CREATE TABLE scores ( student_id INTEGER, course_id INTEGER, score REAL CHECK(score >= 0 AND score <= 100), PRIMARY KEY (student_id, course_id), FOREIGN KEY (student_id) REFERENCES students(id), FOREIGN KEY (course_id) REFERENCES courses(id) );核心功能实现
- 添加学生信息
int add_student(sqlite3 *db, const char *name, const char *gender, int class_id) { sqlite3_stmt *stmt; const char *sql = "INSERT INTO students (name, gender, class_id) VALUES (?, ?, ?);"; int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); if(rc != SQLITE_OK) return rc; sqlite3_bind_text(stmt, 1, name, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, gender, -1, SQLITE_STATIC); sqlite3_bind_int(stmt, 3, class_id); rc = sqlite3_step(stmt); sqlite3_finalize(stmt); return rc; }- 查询班级平均分
float get_class_avg_score(sqlite3 *db, int class_id) { sqlite3_stmt *stmt; const char *sql = "SELECT AVG(s.score) FROM scores s " "JOIN students st ON s.student_id = st.id " "WHERE st.class_id = ?;"; int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); if(rc != SQLITE_OK) return -1; sqlite3_bind_int(stmt, 1, class_id); float avg = -1; if(sqlite3_step(stmt) == SQLITE_ROW) { avg = sqlite3_column_double(stmt, 0); } sqlite3_finalize(stmt); return avg; }- 数据统计报表
void generate_report(sqlite3 *db) { const char *sql = "SELECT c.name AS class_name, " "COUNT(st.id) AS student_count, " "AVG(sc.score) AS avg_score, " "MAX(sc.score) AS max_score, " "MIN(sc.score) AS min_score " "FROM classes c " "LEFT JOIN students st ON c.id = st.class_id " "LEFT JOIN scores sc ON st.id = sc.student_id " "GROUP BY c.id;"; sqlite3_stmt *stmt; int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); if(rc != SQLITE_OK) return; printf("班级\t\t学生数\t平均分\t最高分\t最低分\n"); printf("------------------------------------------------\n"); while(sqlite3_step(stmt) == SQLITE_ROW) { const char *class_name = sqlite3_column_text(stmt, 0); int student_count = sqlite3_column_int(stmt, 1); double avg_score = sqlite3_column_double(stmt, 2); double max_score = sqlite3_column_double(stmt, 3); double min_score = sqlite3_column_double(stmt, 4); printf("%-10s\t%d\t%.1f\t%.1f\t%.1f\n", class_name, student_count, avg_score, max_score, min_score); } sqlite3_finalize(stmt); }7.2 嵌入式设备数据采集系统
在资源受限的嵌入式环境中,SQLite是一个理想的数据存储解决方案。以下是一个数据采集系统的实现要点:
- 数据库设计优化
-- 精简的数据表设计 CREATE TABLE sensor_data ( timestamp INTEGER PRIMARY KEY, sensor_id INTEGER, value REAL, status INTEGER ) WITHOUT ROWID; -- 节省存储空间- 高效数据插入
// 批量插入传感器数据 void batch_insert_sensor_data(sqlite3 *db, SensorData *data, int count) { sqlite3_exec(db, "BEGIN TRANSACTION;", 0, 0, 0); sqlite3_stmt *stmt; const char *sql = "INSERT INTO sensor_data VALUES (?, ?, ?, ?);"; sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); for(int i = 0; i < count; i++) { sqlite3_bind_int(stmt, 1, data[i].timestamp); sqlite3_bind_int(stmt, 2, data[i].sensor_id); sqlite3_bind_double(stmt, 3, data[i].value); sqlite3_bind_int(stmt, 4, data[i].status); sqlite3_step(stmt); sqlite3_reset(stmt); } sqlite3_finalize(stmt); sqlite3_exec(db, "COMMIT;", 0, 0, 0); }- 数据压缩与归档
// 定期归档旧数据 void archive_old_data(sqlite3 *db, time_t cutoff) { // 创建归档表 sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS sensor_data_archive AS SELECT * FROM sensor_data WHERE 0;", 0, 0, 0); // 移动旧数据到归档表 char sql[256]; snprintf(sql, sizeof(sql), "INSERT INTO sensor_data_archive SELECT * FROM sensor_data WHERE timestamp < %ld;", cutoff); sqlite3_exec(db, sql, 0, 0, 0); // 删除已归档数据 snprintf(sql, sizeof(sql), "DELETE FROM sensor_data WHERE timestamp < %ld;", cutoff); sqlite3_exec(db, sql, 0, 0, 0); // 优化数据库 sqlite3_exec(db, "VACUUM;", 0, 0, 0); }8. 高级特性与扩展
8.1 自定义SQL函数
SQLite允许用C语言创建自定义函数,然后在SQL中调用:
// 自定义函数:计算字符串的MD5值 void md5_func(sqlite3_context *context, int argc, sqlite3_value **argv) { const char *input = (const char *)sqlite3_value_text(argv[0]); if(!input) { sqlite3_result_null(context); return; } // 这里应该是实际的MD5计算代码 char md5[33] = "d41d8cd98f00b204e9800998ecf8427e"; // 示例值 sqlite3_result_text(context, md5, -1, SQLITE_TRANSIENT); } // 注册自定义函数 sqlite3_create_function(db, "md5", 1, SQLITE_UTF8, NULL, &md5_func, NULL, NULL); // 在SQL中使用 // SELECT name, md5(name) FROM students;8.2 虚拟表
虚拟表是SQLite的高级特性,允许将外部数据源表示为SQL表:
// 实现一个简单的CSV文件虚拟表 #include <sqlite3ext.h> SQLITE_EXTENSION_INIT1 typedef struct { sqlite3_vtab base; // 自定义数据结构 } CSVVTab; typedef struct { sqlite3_vtab_cursor base; // 游标状态 } CSVVCursor; // 实现虚拟表的各种方法 static int csvvConnect(sqlite3 *db, void *pAux, int argc, const char *const*argv, sqlite3_vtab **ppVTab, char **pzErr) { // 创建虚拟表结构 return SQLITE_OK; } // 注册模块 int sqlite3_csvvtab_init(sqlite3 *db, char **pzErrMsg, const sqlite3_api_routines *pApi) { SQLITE_EXTENSION_INIT2(pApi) static sqlite3_module csvModule = { 0, // iVersion csvvConnect, // xCreate/xConnect // 其他方法实现... }; return sqlite3_create_module(db, "csv", &csvModule, 0); } // 使用虚拟表 // CREATE VIRTUAL TABLE temp.csv_data USING csv(filename='data.csv'); // SELECT * FROM csv_data;8.3 扩展与插件
SQLite支持通过扩展添加功能,如加密、JSON支持等:
// 加载扩展的示例 int rc = sqlite3_load_extension(db, "./sqlite3_extensions/json1", NULL, &err_msg); if(rc != SQLITE_OK) { fprintf(stderr, "无法加载扩展: %s\n", err_msg); sqlite3_free(err_msg); } // 使用JSON扩展 // SELECT json_extract(data, '$.name') FROM documents;9. 跨平台开发注意事项
9.1 文件路径处理
在不同操作系统中,文件路径表示方法不同:
// 跨平台路径处理 const char *get_db_path(const char *base_dir, const char *db_name) { static char path[1024]; #if defined(_WIN32) snprintf(path, sizeof(path), "%s\\%s", base_dir, db_name); #else snprintf(path, sizeof(path), "%s/%s", base_dir, db_name); #endif return path; }9.2 线程安全
SQLite支持多线程访问,但需要正确配置:
// 初始化线程安全模式 int rc = sqlite3_config(SQLITE_CONFIG_MULTITHREAD); if(rc == SQLITE_OK) { rc = sqlite3_initialize(); } // 每个线程使用独立的数据库连接 void *worker_thread(void *arg) { sqlite3 *db; int rc = sqlite3_open("test.db", &db); if(rc != SQLITE_OK) { // 错误处理 return NULL; } // 线程工作... sqlite3_close(db); return NULL; }9.3 平台特定优化
// 针对不同平台设置PRAGMA void configure_database(sqlite3 *db) { #if defined(__linux__) && !defined(ANDROID) // Linux服务器环境优化 sqlite3_exec(db, "PRAGMA journal_mode = WAL;", 0, 0, 0); sqlite3_exec(db, "PRAGMA synchronous = NORMAL;", 0, 0, 0); sqlite3_exec(db, "PRAGMA cache_size = -20000;", 0, 0, 0); // 20MB缓存 #elif defined(_WIN32) // Windows环境设置 sqlite3_exec(db, "PRAGMA temp_store = MEMORY;", 0, 0, 0); #else // 嵌入式或其他环境 sqlite3_exec(db, "PRAGMA journal_mode = MEMORY;", 0, 0, 0); sqlite3_exec(db, "PRAGMA synchronous = OFF;", 0, 0, 0); #endif }10. 调试与性能分析
10.1 SQLite调试技巧
- 启用SQLite日志:
// 设置SQLite日志回调 void sqlite_log(void *pArg, int code, const char *msg) { printf("SQLite日志[%d]: %s\n", code, msg); } // 在打开数据库后设置 sqlite3_config(SQLITE_CONFIG_LOG, sqlite_log, NULL);- 解释查询计划:
-- 查看SQL语句的执行计划 EXPLAIN QUERY PLAN SELECT * FROM students WHERE class_id = 1;- 性能分析:
// 启用性能分析 sqlite3_exec(db, "PRAGMA profile;", 0, 0, 0); // 执行查询后会输出性能信息10.2 常见错误处理
- 错误代码解析:
const char *err_str = sqlite3_errstr(rc); printf("错误代码 %d: %s\n", rc, err_str);- 资源泄漏检测:
// 在调试版本中检查资源泄漏 #ifdef DEBUG #define CHECK_LEAKS() do { \ printf("准备语句未释放: %d\n", sqlite3_stmt_status(db, SQLITE_STMTSTATUS_MAX, 0)); \ } while(0) #else #define CHECK_LEAKS() #endif- 内存使用统计:
// 获取内存使用情况 int current = sqlite3_memory_used(); int highwater = sqlite3_memory_highwater(0); printf("内存使用: 当前=%d字节, 峰值=%d字节\n", current, highwater);11. 工具与资源推荐
11.1 图形化管理工具
DB Browser for SQLite:
- 开源跨平台工具
- 提供可视化表设计、数据浏览和SQL编辑功能
SQLiteStudio:
- 功能更丰富的开源工具
- 支持插件扩展
Navicat for SQLite:
- 商业软件,功能强大
- 支持数据同步和导入导出
11.2 性能分析工具
sqlite3_analyzer:
- SQLite官方提供的分析工具
- 统计表和索引的空间使用情况
sqltune:
- 用于分析SQL查询性能
- 提供优化建议
11.3 学习资源
官方文档:
- SQLite官方网站
- 完整的C语言API参考
书籍推荐:
- 《The Definitive Guide to SQLite》
- 《SQLite权威指南》
在线课程:
- Udemy上的SQLite相关课程
- Coursera的数据库基础课程
12. 项目实战:构建完整应用
12.1 需求分析
我们将开发一个个人任务管理系统,具有以下功能:
- 任务分类管理
- 优先级设置
- 截止日期提醒
- 任务搜索与过滤
- 数据备份与恢复
12.2 数据库设计
-- 分类表 CREATE TABLE categories ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, color TEXT ); -- 任务表 CREATE TABLE tasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, description TEXT, priority INTEGER DEFAULT 1 CHECK(priority BETWEEN 1 AND 5), due_date INTEGER, -- Unix时间戳 created_at INTEGER DEFAULT (strftime('%s', 'now')), completed INTEGER DEFAULT 0, category_id INTEGER, FOREIGN KEY (category_id) REFERENCES categories(id) ); -- 子任务表 CREATE TABLE subtasks ( id INTEGER PRIMARY KEY AUTOINCREMENT, task_id INTEGER NOT NULL, description TEXT NOT NULL, completed INTEGER DEFAULT 0, FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE ); -- 创建索引 CREATE INDEX idx_tasks_priority ON tasks(priority); CREATE INDEX idx_tasks_due_date ON tasks(due_date); CREATE INDEX idx_tasks_category ON tasks(category_id);12.3 核心功能实现
初始化数据库
int init_database(const char *db_path) { sqlite3 *db; int rc = sqlite3_open(db_path, &db); if(rc != SQLITE_OK) return rc; // 启用外键约束 sqlite3_exec(db, "PRAGMA foreign_keys = ON;", 0, 0, 0); // 创建表 const char *sql = "CREATE TABLE IF NOT EXISTS categories (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "name TEXT NOT NULL UNIQUE," "color TEXT);" "CREATE TABLE IF NOT EXISTS tasks (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "title TEXT NOT NULL," "description TEXT," "priority INTEGER DEFAULT 1 CHECK(priority BETWEEN 1 AND 5)," "due_date INTEGER," "created_at INTEGER DEFAULT (strftime('%s', 'now'))," "completed INTEGER DEFAULT 0," "category_id INTEGER," "FOREIGN KEY (category_id) REFERENCES categories(id));" "CREATE TABLE IF NOT EXISTS subtasks (" "id INTEGER PRIMARY KEY AUTOINCREMENT," "task_id INTEGER NOT NULL," "description TEXT NOT NULL," "completed INTEGER DEFAULT 0," "FOREIGN KEY (task_id) REFERENCES tasks(id) ON DELETE CASCADE);"; rc = sqlite3_exec(db, sql, 0, 0, 0); sqlite3_close(db); return rc; }添加任务
int add_task(sqlite3 *db, const char *title, const char *desc, int priority, time_t due_date, int category_id) { sqlite3_stmt *stmt; const char *sql = "INSERT INTO tasks (title, description, priority, due_date, category_id) " "VALUES (?, ?, ?, ?, ?);"; int rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL); if(rc != SQLITE_OK) return rc; sqlite3_bind_text(stmt, 1, title, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, desc, -1, SQLITE_STATIC); sqlite3_bind_int(stmt, 3, priority); sqlite3_bind_int64(stmt, 4, due_date); if(category_id > 0) { sqlite3_bind_int(stmt, 5, category_id); } else { sqlite3_bind_null(st