SQLite 教程

SQLite 是世界上部署量最大的数据库:每台手机、每个浏览器、无数桌面软件和 IoT 设备里都跑着它。它不是客户端-服务器架构,而是嵌入式库——整个数据库就是磁盘上的一个文件,零配置、零运维、无网络端口。对 C/C++ 学习者来说,它还是学习数据库内部实现和给程序加持久化能力的最佳选择。


一、定位与适用场景

适合 SQLite不适合 SQLite
移动端 App 本地存储(Android/iOS 内置)高并发写入的服务端
桌面软件(浏览器历史、聊天记录)多机通过网络共享访问
IoT 与嵌入式设备需要精细权限体系的多用户系统
单元测试与原型开发超大并发 OLTP 业务
小型低流量网站

一句话判断:数据只有一个进程访问、或以”文件”形式随程序分发,SQLite 就是首选;多个服务同时读写就上 MySQL/PostgreSQL。


二、sqlite3 命令行快速上手

sqlite3 test.db          # 打开/创建数据库文件

2.1 常用点命令

点命令是 sqlite3 CLI 特有的元命令,以 . 开头且不加分号:

命令作用
.tables列出所有表
.schema 表名查看建表语句
.headers on显示列名
.mode column列对齐显示(还有 csv/json/box 等)
.databases列出当前连接的数据库文件
.dump导出全部 SQL 语句到屏幕
.import file.csv t导入 CSV 到表 t
.read script.sql执行 SQL 脚本
.quit.exit退出

一次典型的会话:

.headers on
.mode column
.mode box
 
CREATE TABLE books (
    id    INTEGER PRIMARY KEY AUTOINCREMENT,
    title TEXT NOT NULL,
    price REAL
);
 
INSERT INTO books (title, price) VALUES ('C 程序设计', 59.9), ('算法导论', 128.0);
 
SELECT * FROM books;

2.2 点命令 vs SQL 的区分

类别特征示例结尾分号
点命令CLI 客户端指令,. 开头.tables .mode column不需要
SQL 语句数据库引擎执行,发给内核解析SELECT ... FROM ...;需要

记住:点命令只在 sqlite3 命令行里有意义,写进程序代码的必须是纯 SQL。


三、类型亲和性规则表

SQLite 的类型系统非常独特:建表时声明的类型只是”建议”,实际存储按**亲和性(Type Affinity)**规则决定。声明列类型时按以下规则映射到五种亲和:

亲和性触发规则(声明类型包含…)存储行为
INTEGER含 “INT”(INT/INTEGER/TINYINT…)尽量存为整数
TEXT含 “CHAR”、“CLOB”、“TEXT”一律转文本存储
BLOB无匹配任何规则(或声明为 BLOB)原样二进制存储,不做转换
REAL含 “REAL”、“FLOA”、“DOUB”转浮点数存储
NUMERIC其余情况(DECIMAL/NUMERIC/NUMBER…)能转数字就转,不能则原样存

实际影响举例:

CREATE TABLE t (a INTEGER, b TEXT, c NUMERIC, d BLOB);
 
INSERT INTO t VALUES ('123', '123', '123', '123');
-- a 列存入整数 123(INTEGER 亲和把文本转成了数字)
-- b 列保持文本 '123'
-- c 列存入整数 123(NUMERIC 亲和优先数字)
-- d 列保持文本 '123'(BLOB 亲和不转换)

这是 SQLite 与其他数据库最大的思维差异:它是动态类型的。MySQL 里往 INT 列塞字符串要么报错要么强转,SQLite 则按亲和性灵活处理。写严谨代码仍应声明正确类型。


四、与 C/C++ 集成最小示例

SQLite 本身就是一个 C 库,Linux 发行版通常自带开发头文件:

# 安装开发包
sudo apt install libsqlite3-dev        # Ubuntu/Debian
sudo pacman -S sqlite                  # Arch 自带头文件
gcc main.c -lsqlite3 -o app            # 编译时链接 -lsqlite3

完整可编译示例,覆盖核心 API 四步走:open → prepare → step → finalize

/* sqlite_demo.c — 编译: gcc sqlite_demo.c -lsqlite3 -o sqlite_demo */
#include <stdio.h>
#include <sqlite3.h>
 
static int callback(void *unused, int argc, char **argv, char **colname)
{
    (void)unused;
    for (int i = 0; i < argc; i++)
        printf("%s = %s\n", colname[i], argv[i] ? argv[i] : "NULL");
    printf("----\n");
    return 0;   /* 返回非 0 可中止查询 */
}
 
int main(void)
{
    sqlite3 *db;
    char *err = NULL;
 
    /* 1. 打开数据库(不存在则创建文件) */
    if (sqlite3_open("test.db", &db) != SQLITE_OK) {
        fprintf(stderr, "open failed: %s\n", sqlite3_errmsg(db));
        return 1;
    }
 
    /* 2. 建表并插入数据 */
    const char *setup_sql =
        "CREATE TABLE IF NOT EXISTS students ("
        "  id   INTEGER PRIMARY KEY AUTOINCREMENT,"
        "  name TEXT NOT NULL,"
        "  age  INTEGER);"
        "DELETE FROM students;"
        "INSERT INTO students (name, age) VALUES ('Alice', 18), ('Bob', 19);";
    if (sqlite3_exec(db, setup_sql, NULL, NULL, &err) != SQLITE_OK) {
        fprintf(stderr, "exec failed: %s\n", err);
        sqlite3_free(err);
        sqlite3_close(db);
        return 1;
    }
 
    /* 3. 参数化查询:prepare -> bind -> step -> finalize */
    sqlite3_stmt *stmt;
    const char *query = "SELECT id, name, age FROM students WHERE age > ?;";
    if (sqlite3_prepare_v2(db, query, -1, &stmt, NULL) != SQLITE_OK) {
        fprintf(stderr, "prepare failed: %s\n", sqlite3_errmsg(db));
        sqlite3_close(db);
        return 1;
    }
    sqlite3_bind_int(stmt, 1, 17);          /* 第 1 个 ? 绑定为 17 */
 
    while ((sqlite3_step(stmt)) == SQLITE_ROW) {      /* 逐行取结果 */
        int id   = sqlite3_column_int(stmt, 0);
        const unsigned char *name = sqlite3_column_text(stmt, 1);
        int age  = sqlite3_column_int(stmt, 2);
        printf("%d %s %d\n", id, name, age);
    }
    sqlite3_finalize(stmt);                   /* 释放语句对象 */
 
    /* 4. 回调方式执行查询(另一种风格) */
    sqlite3_exec(db, "SELECT * FROM students;", callback, NULL, &err);
 
    sqlite3_close(db);                        /* 关闭连接 */
    return 0;
}

核心 API 速查:

API职责
sqlite3_open() / sqlite3_close()打开/关闭数据库连接
sqlite3_exec()一把梭执行 SQL + 可选回调(适合无结果集语句)
sqlite3_prepare_v2()把 SQL 编译为语句对象
sqlite3_bind_int/text()? 占位符绑定参数,防 SQL 注入的正确姿势
sqlite3_step()推进一行;SQLITE_ROW 表示有数据,SQLITE_DONE 表示结束
sqlite3_column_*()取当前行某列的值
sqlite3_finalize()销毁语句对象

五、Python sqlite3 模块示例

Python 标准库内置 SQLite 支持,无需安装任何东西:

import sqlite3
 
# 连接(文件不存在自动创建;":memory:" 为内存数据库)
conn = sqlite3.connect("test.db")
cur = conn.cursor()
 
cur.execute("""
    CREATE TABLE IF NOT EXISTS students (
        id   INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        age  INTEGER
    )
""")
 
# 参数化插入(用 ? 占位符,防注入)
cur.execute("INSERT INTO students (name, age) VALUES (?, ?)", ("Carol", 20))
cur.executemany(
    "INSERT INTO students (name, age) VALUES (?, ?)",
    [("Dave", 18), ("Eve", 19)],
)
conn.commit()                       # 写操作必须 commit
 
# 查询
cur.execute("SELECT id, name, age FROM students WHERE age >= ?", (18,))
for row in cur.fetchall():
    print(row)
 
cur.close()
conn.close()
 
# 更省事的上下文管理器写法
with sqlite3.connect("test.db") as conn:
    for row in conn.execute("SELECT name FROM students"):
        print(row[0])

六、常见限制

限制说明
并发写弱整库同一时刻只支持一个写者(WAL 模式下读可并行),高并发写场景直接淘汰
无网络访问数据库就是本地文件,跨机器共享需应用层自己解决(强行放 NFS 上会损坏数据)
权限体系缺失没有用户、角色、GRANT,谁能读到文件谁就有全部权限,靠文件系统权限兜底
单文件体积建议上限官方支持到 TB 级,但实践中超过几十 GB 后运维工具链就不如服务端数据库顺手
部分 SQL 特性缺失无存储过程、原生不支持 RIGHT/FULL JOIN(新版已补)、ALTER 能力有限

这些限制不是缺陷而是取舍——省掉的一切换来了零配置和极致轻量(核心几百 KB)。


七、备份

7.1 .backup 在线备份命令

.backup backup.db       -- 在线安全备份为另一个文件(点命令)
# 命令行等价形式
sqlite3 test.db ".backup /backup/test_backup.db"

7.2 dump 导出 SQL 文本

# 全库导出为 SQL 语句
sqlite3 test.db .dump > backup.sql
 
# 只导出指定表
sqlite3 test.db ".dump students" > students.sql
 
# 恢复(重放 SQL)
sqlite3 newdb.db < backup.sql
 
# 定期备份 cron 示例:每天凌晨拷贝一份带日期的备份
# crontab: 30 3 * * * sqlite3 /app/data/app.db ".backup /backup/app_$(date +\%F).db"

由于数据库就是单个文件,最简单的冷备就是停服后直接 cp——这也是嵌入式方案的运维优势之一。在线热备请一律使用 .backup 而不是直接复制正在被写的文件。


八、本章小结

主题要点
定位嵌入式单文件、零配置,部署量世界第一
场景移动端/桌面/IoT/测试选它,多进程高并发写避开它
CLI点命令管客户端(.tables/.mode),SQL 分号结尾管数据
类型五种亲和性 INTEGER/TEXT/BLOB/REAL/NUMERIC,动态类型系统
C 集成open → prepare → bind → step → finalize,链接 -lsqlite3
备份在线用 .backup,冷备直接 cp,恢复重放 dump

返回 数据库目录