SQLite 从入门到实战:零配置嵌入式数据库完整教程

SQLite 从入门到实战:零配置嵌入式数据库完整教程

实验环境 :华为云 FlexusX ecs-9fdf-0001 · Ubuntu 24.04.4 LTS · SQLite 3.45.1 · Python 3.12.3

本文特点:命令均在真实服务器上执行,输出为实机截图,非伪造。


一、SQLite 是什么

SQLite 是全球部署量最大的数据库引擎,没有之一。它不需要独立的服务进程,整个数据库是一个普通文件(.db),应用程序直接通过 C 库调用 SQL。

复制代码
┌──────────────────────────────────────────────────────┐
│                   应用程序 (Python/C/Java...)         │
├──────────────────────────────────────────────────────┤
│                 SQLite 库 (libsqlite3)                │
├──────────────────────────────────────────────────────┤
│                  数据库文件 (*.db)                    │
│             ┌──────────┬──────────┐                  │
│             │  数据页  │  索引页  │                  │
│             └──────────┴──────────┘                  │
└──────────────────────────────────────────────────────┘

SQLite vs 主流数据库对比

特性 SQLite MySQL PostgreSQL
部署方式 嵌入式(无服务器) C/S 架构 C/S 架构
配置复杂度 零配置 需要配置 需要配置
存储形式 单文件 数据目录 数据目录
并发写入 较弱(WAL 模式改善)
适用场景 嵌入式/移动/测试 中大型 Web 复杂事务/大数据
数据类型系统 动态类型 静态类型 静态类型

适合 SQLite 的场景

  • 移动端 App(Android/iOS 原生 DB)
  • 桌面软件本地存储(Chrome 书签、VSCode 历史)
  • 开发/测试替代真实数据库
  • 物联网设备、嵌入式系统
  • 中小规模 Web(Django 默认 DB 即为 SQLite)

二、安装

Ubuntu/Debian

bash 复制代码
# Ubuntu 24.04 apt 直接安装
apt-get install -y sqlite3

# 验证版本
sqlite3 --version

实机输出

复制代码
3.45.1 2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257ccalt1 (64-bit)

CentOS/RHEL

bash 复制代码
yum install -y sqlite sqlite-devel
# 或 dnf
dnf install -y sqlite

macOS

bash 复制代码
# macOS 自带,或用 Homebrew 升级
brew install sqlite

Python 内置支持

Python 标准库自带 sqlite3 模块,无需额外安装

bash 复制代码
python3 -c "import sqlite3; print(sqlite3.sqlite_version)"
# 3.45.1

三、SQLite 命令行基础

3.1 进入交互式 shell

bash 复制代码
# 打开(不存在则自动创建)数据库文件
sqlite3 /tmp/school.db

# 直接执行 SQL 文件
sqlite3 /tmp/school.db < init.sql

# 通过 heredoc 执行多条语句(脚本中常用)
sqlite3 /tmp/school.db << 'EOF'
SELECT sqlite_version();
.tables
EOF

3.2 常用 Dot 命令(. 开头,不是 SQL)

命令 说明
.tables 列出所有表
.schema 表名 查看建表语句
.databases 列出已附加数据库
.mode column 列对齐格式输出
.headers on 显示列名
.output file.txt 输出重定向到文件
.read file.sql 执行 SQL 文件
.quit / .exit 退出
.help 查看帮助
sql 复制代码
sqlite> .headers on
sqlite> .mode column
sqlite> SELECT * FROM students LIMIT 2;
id  name  age  grade  score
--  ----  ---  -----  -----
1   张伟   20   A      92.5
2   李娜   19   B      85.0

四、数据库与表操作

4.1 创建数据库

SQLite 中"创建数据库"等同于打开一个文件:

bash 复制代码
sqlite3 /tmp/school.db

文件不存在时自动创建,退出(.quit)后数据持久化。

4.2 创建表

以"学生选课"场景为例,建立三张表:

sql 复制代码
-- 学生表
CREATE TABLE students (
    id      INTEGER PRIMARY KEY AUTOINCREMENT,  -- 自增主键
    name    TEXT    NOT NULL,                   -- 非空约束
    age     INTEGER,
    grade   TEXT,
    score   REAL,
    email   TEXT    UNIQUE                      -- 唯一约束
);

-- 课程表
CREATE TABLE courses (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    course_name TEXT    NOT NULL,
    teacher     TEXT,
    credits     INTEGER DEFAULT 2               -- 默认值
);

-- 选课关系表(联合主键 + 外键)
CREATE TABLE enrollments (
    student_id INTEGER,
    course_id  INTEGER,
    enroll_date TEXT,
    PRIMARY KEY (student_id, course_id),
    FOREIGN KEY (student_id) REFERENCES students(id),
    FOREIGN KEY (course_id)  REFERENCES courses(id)
);

实机输出(验证建表结果):

复制代码
courses      enrollments  students

查看表结构:

sql 复制代码
PRAGMA table_info(students);
复制代码
0|id|INTEGER|0||1
1|name|TEXT|1||0
2|age|INTEGER|0||0
3|grade|TEXT|0||0
4|score|REAL|0||0
5|email|TEXT|0||0

📌 踩坑 :SQLite 的 DEFAULT 值只能是常量(字符串/数字),不能用 date('now') 这类函数表达式,否则报错:

Error: in prepare, default value of column [xxx] is not constant

4.3 删除表

sql 复制代码
DROP TABLE IF EXISTS old_table;

IF EXISTS 防止表不存在时报错。


五、数据类型系统

SQLite 使用"动态类型"(Type Affinity,类型亲缘性),共 5 种存储类:

存储类 说明 对应 SQL 类型
NULL 空值 NULL
INTEGER 有符号整数,1/2/3/4/6/8 字节 INT, INTEGER, TINYINT...
REAL 8字节 IEEE 浮点 REAL, FLOAT, DOUBLE
TEXT UTF-8/16/GB2312 字符串 TEXT, CHAR, VARCHAR...
BLOB 二进制数据 BLOB
sql 复制代码
-- SQLite 允许任意类型的值存入任意列(灵活也是风险)
CREATE TABLE demo (col TEXT);
INSERT INTO demo VALUES (42);       -- 整数存入 TEXT 列:合法
INSERT INTO demo VALUES (3.14);     -- 浮点:合法
INSERT INTO demo VALUES ('hello');  -- 字符串:合法
INSERT INTO demo VALUES (NULL);     -- NULL:合法

六、CRUD 基础操作

6.1 INSERT --- 插入数据

sql 复制代码
-- 单行插入
INSERT INTO students (name, age, grade, score, email)
VALUES ('张伟', 20, 'A', 92.5, 'zhangwei@example.com');

-- 多行批量插入(SQLite 3.7.11+)
INSERT INTO students (name, age, grade, score, email) VALUES
('李娜', 19, 'B', 85.0, 'lina@example.com'),
('王芳', 21, 'A', 95.0, 'wangfang@example.com'),
('刘洋', 22, 'C', 70.5, 'liuyang@example.com'),
('陈静', 20, 'B', 88.5, 'chenjing@example.com'),
('赵磊', 23, 'A', 91.0, 'zhaolei@example.com'),
('孙丽', 19, 'C', 65.0, 'sunli@example.com'),
('周杰', 21, 'B', 82.0, 'zhoujie@example.com');

6.2 SELECT --- 查询数据

sql 复制代码
-- 基础查询
SELECT id, name, age, grade, score FROM students;

实机输出

复制代码
1|张伟|20|A|92.5
2|李娜|19|B|85.0
3|王芳|21|A|95.0
4|刘洋|22|C|70.5
5|陈静|20|B|88.5
6|赵磊|23|A|91.0
7|孙丽|19|C|65.0
8|周杰|21|B|82.0

6.3 WHERE --- 条件过滤

sql 复制代码
-- 简单条件
SELECT name, age, score FROM students WHERE score >= 85.0;
复制代码
张伟|20|92.5
李娜|19|85.0
王芳|21|95.0
陈静|20|88.5
赵磊|23|91.0
sql 复制代码
-- AND / OR 复合条件
SELECT name, grade, score
FROM students
WHERE grade = 'A' OR (grade = 'B' AND score > 87);
复制代码
张伟|A|92.5
王芳|A|95.0
陈静|B|88.5
赵磊|A|91.0

6.4 UPDATE --- 更新数据

sql 复制代码
UPDATE students SET score = 75.0, grade = 'C' WHERE name = '刘洋';

验证:

复制代码
刘洋|C|75.0

6.5 DELETE --- 删除数据

sql 复制代码
-- 条件删除
DELETE FROM students WHERE grade = 'C' AND score < 68;

-- 删除全表数据(不删表结构)
DELETE FROM students;

-- ⚠️ SQLite 没有 TRUNCATE,用 DELETE FROM 代替
-- 如果要重置 AUTOINCREMENT 计数:
DELETE FROM sqlite_sequence WHERE name='students';

七、查询进阶

7.1 LIKE --- 模糊匹配

通配符 含义
% 任意个任意字符
_ 单个任意字符
sql 复制代码
SELECT name, email FROM students WHERE email LIKE '%example.com';
复制代码
张伟|zhangwei@example.com
李娜|lina@example.com
王芳|wangfang@example.com
... (8行)

7.2 GLOB --- 大小写敏感通配符

GLOB 区分大小写,使用 Unix shell 风格通配符:

sql 复制代码
-- * 匹配任意字符,? 匹配单个字符,[abc] 字符集
SELECT name, email FROM students WHERE email GLOB 'z*';
复制代码
张伟|zhangwei@example.com
赵磊|zhaolei@example.com
周杰|zhoujie@example.com

LIKE 不区分大小写(默认),GLOB 区分大小写。

7.3 ORDER BY --- 排序

sql 复制代码
-- 降序
SELECT name, score FROM students ORDER BY score DESC;

-- 多字段排序
SELECT name, grade, score FROM students ORDER BY grade ASC, score DESC;
复制代码
王芳|95.0
张伟|92.5
赵磊|91.0
陈静|88.5
李娜|85.0
周杰|82.0
刘洋|75.0
孙丽|65.0

7.4 LIMIT / OFFSET --- 分页

sql 复制代码
-- 第1页(前3条)
SELECT name, score FROM students ORDER BY score DESC LIMIT 3;
-- 王芳|95.0  张伟|92.5  赵磊|91.0

-- 第2页(跳过3条,取3条)
SELECT name, score FROM students ORDER BY score DESC LIMIT 3 OFFSET 3;
-- 陈静|88.5  李娜|85.0  周杰|82.0

7.5 GROUP BY + 聚合函数

函数 说明
COUNT(*) 行数
AVG(col) 平均值
SUM(col) 求和
MAX(col) 最大值
MIN(col) 最小值
sql 复制代码
SELECT grade,
       COUNT(*)   AS 人数,
       AVG(score) AS 平均分,
       MAX(score) AS 最高分,
       MIN(score) AS 最低分
FROM students
GROUP BY grade
ORDER BY grade;

实机输出

复制代码
A|3|92.8333333333333|95.0|91.0
B|3|85.1666666666667|88.5|82.0
C|2|67.75|70.5|65.0

7.6 HAVING --- 分组后过滤

sql 复制代码
-- 筛选平均分 >= 80 的等级组
SELECT grade, COUNT(*) AS 人数, AVG(score) AS 平均分
FROM students
GROUP BY grade
HAVING AVG(score) >= 80
ORDER BY 平均分 DESC;
复制代码
A|3|92.8333333333333
B|3|85.1666666666667

📌 区分 WHEREHAVING:WHERE 在分组前过滤行,HAVING 在分组后过滤组。

7.7 DISTINCT --- 去重

sql 复制代码
SELECT DISTINCT grade FROM students ORDER BY grade;
复制代码
A
B
C

八、多表查询(JOIN)

8.1 INNER JOIN --- 内连接

只返回两表中匹配的记录:

sql 复制代码
SELECT s.name, c.course_name, e.enroll_date
FROM students s
INNER JOIN enrollments e ON s.id = e.student_id
INNER JOIN courses c ON e.course_id = c.id
ORDER BY s.name;

实机输出

复制代码
刘洋|数据库原理|2026-03-03
周杰|操作系统|2026-03-03
周杰|计算机网络|2026-03-03
孙丽|数据库原理|2026-03-04
张伟|数据库原理|2026-03-01
张伟|操作系统|2026-03-01
李娜|数据库原理|2026-03-02
李娜|计算机网络|2026-03-02
王芳|操作系统|2026-03-01
王芳|数据结构|2026-03-01
赵磊|数据结构|2026-03-01
陈静|计算机网络|2026-03-02

8.2 LEFT JOIN --- 左连接

保留左表所有记录,右表无匹配时为 NULL:

sql 复制代码
-- 统计每个学生的选课数(未选课学生也要显示)
SELECT s.name, COUNT(e.course_id) AS 选课数
FROM students s
LEFT JOIN enrollments e ON s.id = e.student_id
GROUP BY s.id, s.name
ORDER BY 选课数 DESC;
复制代码
张伟|2
李娜|2
王芳|2
周杰|2
刘洋|1
陈静|1
赵磊|1
孙丽|1

JOIN 类型速查图:

students(左)    enrollments(右)
  A ─────────────── A    ← INNER JOIN:只取交集
  B ─────────────── B
  C ─────────────── ✗    ← LEFT JOIN:C 也保留,右边为 NULL
  ✗ ─────────────── D    ← RIGHT JOIN:D 也保留(SQLite 不支持)

⚠️ SQLite 不支持 RIGHT JOIN 和 FULL OUTER JOIN。需要时用 LEFT JOIN + UNION 模拟。

8.3 别名(Alias)

sql 复制代码
-- 列别名
SELECT name AS 姓名, score AS 成绩 FROM students;

-- 表别名(简化多表查询)
SELECT s.name, c.course_name
FROM students AS s
JOIN enrollments AS e ON s.id = e.student_id
JOIN courses AS c ON e.course_id = c.id;

九、子查询

9.1 WHERE 子查询

sql 复制代码
-- 查询分数高于平均分的学生
SELECT name, score
FROM students
WHERE score > (SELECT AVG(score) FROM students)
ORDER BY score DESC;
复制代码
王芳|96.0
张伟|92.5
赵磊|91.0
陈静|88.5
李娜|85.0

9.2 SELECT 子查询(标量子查询)

sql 复制代码
-- 每门课程的选课人数
SELECT course_name,
       (SELECT COUNT(*) FROM enrollments WHERE course_id = c.id) AS 选课人数
FROM courses c
ORDER BY 选课人数 DESC;
复制代码
数据库原理|4
操作系统|3
计算机网络|3
数据结构|2

十、索引

10.1 为什么需要索引

复制代码
无索引:全表扫描 O(n)
有索引:B-Tree 搜索 O(log n)

students 表 (8行):差异不明显
实际场景 100万行:全表扫描 vs 索引 = 秒级 vs 毫秒级

10.2 创建索引

sql 复制代码
-- 普通索引
CREATE INDEX idx_score ON students(score);
CREATE INDEX idx_grade ON students(grade);

-- 唯一索引(允许 NULL,NULL 不参与唯一检查)
CREATE UNIQUE INDEX idx_email ON students(email) WHERE email IS NOT NULL;

-- 复合索引
CREATE INDEX idx_grade_score ON students(grade, score);

10.3 验证索引效果(EXPLAIN QUERY PLAN)

sql 复制代码
EXPLAIN QUERY PLAN SELECT name, score FROM students WHERE score > 85;

实机输出

复制代码
QUERY PLAN
`--SEARCH students USING INDEX idx_score (score>?)

没有索引时输出会是:

复制代码
`--SCAN students

10.4 查看/删除索引

sql 复制代码
-- 查看所有索引
SELECT name, tbl_name, sql FROM sqlite_master WHERE type='index' ORDER BY name;
复制代码
idx_email|students|CREATE UNIQUE INDEX idx_email ON students(email) WHERE email IS NOT NULL
idx_grade|students|CREATE INDEX idx_grade ON students(grade)
idx_score|students|CREATE INDEX idx_score ON students(score)
sqlite_autoindex_enrollments_1|enrollments|
sqlite_autoindex_students_1|students|
sql 复制代码
-- 删除索引
DROP INDEX IF EXISTS idx_grade;

十一、视图(View)

视图是存储在数据库中的 SELECT 语句,像虚拟表一样使用:

sql 复制代码
CREATE VIEW v_student_summary AS
SELECT s.name,
       s.grade,
       s.score,
       COUNT(e.course_id) AS enrolled_courses
FROM students s
LEFT JOIN enrollments e ON s.id = e.student_id
GROUP BY s.id, s.name, s.grade, s.score;

像普通表一样查询:

sql 复制代码
SELECT * FROM v_student_summary ORDER BY score DESC;
复制代码
王芳|A|96.0|2
张伟|A|92.5|2
赵磊|A|91.0|1
陈静|B|88.5|1
李娜|B|85.0|2
周杰|B|82.0|2
刘洋|C|75.0|1
孙丽|C|65.0|1
sql 复制代码
-- 删除视图
DROP VIEW IF EXISTS v_student_summary;

⚠️ SQLite 视图是只读的,不支持 INSERT/UPDATE/DELETE(可以用触发器绕过)。


十二、触发器(Trigger)

触发器在特定数据库事件(INSERT/UPDATE/DELETE)发生时自动执行:

sql 复制代码
-- 审计日志表
CREATE TABLE audit_log (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    action      TEXT,
    student_name TEXT,
    old_score   REAL,
    new_score   REAL,
    changed_at  TEXT
);

-- 当 students.score 被修改时,自动记录审计日志
CREATE TRIGGER trg_score_update
AFTER UPDATE OF score ON students
FOR EACH ROW
BEGIN
    INSERT INTO audit_log (action, student_name, old_score, new_score, changed_at)
    VALUES ('UPDATE', NEW.name, OLD.score, NEW.score, datetime('now', 'localtime'));
END;

触发测试:

sql 复制代码
UPDATE students SET score = 96.0 WHERE name = '王芳';
SELECT action, student_name, old_score, new_score FROM audit_log;

实机输出

复制代码
UPDATE|王芳|95.0|96.0

触发器关键字说明:

关键字 说明
BEFORE / AFTER 在事件前/后执行
FOR EACH ROW 每行变更都触发
OLD.列名 变更前的值(UPDATE/DELETE 可用)
NEW.列名 变更后的值(INSERT/UPDATE 可用)

十三、事务(Transaction)

SQLite 的事务保证 ACID 特性:

sql 复制代码
-- ✅ 成功提交
BEGIN TRANSACTION;
INSERT INTO students (name, age, grade, score, email)
VALUES ('事务测试A', 20, 'A', 90.0, 'ta@test.com');
INSERT INTO students (name, age, grade, score, email)
VALUES ('事务测试B', 21, 'B', 80.0, 'tb@test.com');
COMMIT;
-- 实机输出:两条记录均插入成功
sql 复制代码
-- ❌ 回滚:数据不写入
BEGIN TRANSACTION;
INSERT INTO students (name, age, grade, score, email)
VALUES ('回滚测试', 20, 'C', 60.0, 'rollback@test.com');
ROLLBACK;

SELECT COUNT(*) FROM students WHERE name='回滚测试';
-- 实机输出:0(数据被回滚,不存在)

性能提示:批量 INSERT 默认每条都是独立事务,极慢。用显式事务包裹:

sql 复制代码
BEGIN;
INSERT INTO t VALUES (1);
INSERT INTO t VALUES (2);
-- ... 几千条
COMMIT;  -- 一次性提交,速度提升 100x+

十四、约束(Constraints)

sql 复制代码
CREATE TABLE demo_constraints (
    id      INTEGER PRIMARY KEY,          -- 主键约束(隐含 NOT NULL + UNIQUE)
    name    TEXT    NOT NULL,             -- 非空约束
    email   TEXT    UNIQUE,              -- 唯一约束
    age     INTEGER CHECK (age >= 0 AND age <= 150),  -- 检查约束
    dept_id INTEGER REFERENCES dept(id), -- 外键约束
    score   REAL    DEFAULT 0.0          -- 默认值
);

⚠️ SQLite 默认不强制外键约束,需要开启:

sql 复制代码
PRAGMA foreign_keys = ON;

每次连接都要执行,不会持久化。


十五、ALTER TABLE

SQLite 的 ALTER TABLE 功能相比 MySQL 较受限:

sql 复制代码
-- ✅ 支持:添加列
ALTER TABLE students ADD COLUMN phone TEXT;
ALTER TABLE students ADD COLUMN created_at TEXT DEFAULT '2026-01-01';

验证:

复制代码
PRAGMA table_info(students);

0|id|INTEGER|0||1
1|name|TEXT|1||0
...
6|phone|TEXT|0||0
7|created_at|TEXT|0|'2026-01-01'|0
sql 复制代码
-- ✅ 支持:重命名表
ALTER TABLE students RENAME TO students_old;

-- ✅ 支持(3.25.0+):重命名列
ALTER TABLE students RENAME COLUMN email TO email_addr;

-- ❌ 不支持:删除列(需重建表)
-- 变通方案:建新表 → 拷贝数据 → 删旧表 → 改名

十六、PRAGMA --- 数据库配置

sql 复制代码
-- 查看数据库文件信息
PRAGMA database_list;
复制代码
0|main|/tmp/school.db
sql 复制代码
-- 查看表结构
PRAGMA table_info(students);

-- 开启外键支持(每次连接都需要)
PRAGMA foreign_keys = ON;

-- WAL 模式(提升并发写性能)
PRAGMA journal_mode = WAL;

-- 同步模式(NORMAL 在性能与安全之间平衡)
PRAGMA synchronous = NORMAL;

-- 页面缓存大小(单位:页,默认 2000 = ~8MB)
PRAGMA cache_size = 10000;

-- 查看页大小
PRAGMA page_size;  -- 默认 4096 字节

十七、ATTACH / DETACH --- 附加数据库

SQLite 支持在同一连接中同时操作多个数据库文件:

sql 复制代码
-- 附加备份数据库
ATTACH DATABASE '/tmp/backup.db' AS backup;

-- 跨库操作:将 main 的数据复制到 backup
CREATE TABLE backup.students_backup AS
SELECT * FROM main.students;

-- 验证
SELECT COUNT(*) FROM backup.students_backup;
-- 实机输出:8

-- 分离
DETACH DATABASE backup;

十八、NULL 值处理

sql 复制代码
-- 插入含 NULL 的记录
INSERT INTO students (name, age, grade, score) VALUES ('无邮箱学生', 18, 'B', 80.0);
-- email 列未赋值,默认 NULL

-- ✅ 正确:IS NULL / IS NOT NULL
SELECT name, email FROM students WHERE email IS NULL;
-- 无邮箱学生|

-- ❌ 错误:NULL != NULL,以下查不到结果
SELECT name FROM students WHERE email = NULL;  -- 永远为空

-- COALESCE:返回第一个非 NULL 值
SELECT name, COALESCE(email, '暂无邮箱') AS email FROM students LIMIT 3;

十九、日期 & 时间函数

SQLite 没有专用 DATETIME 类型,用 TEXT/INTEGER/REAL 存储,提供内置函数处理:

sql 复制代码
SELECT
    date('now')                              AS 今天,
    datetime('now', 'localtime')            AS 本地时间,
    strftime('%Y年%m月%d日', 'now', 'localtime') AS 中文格式,
    julianday('now') - julianday('2026-01-01') AS 今年已过天数;

实机输出

复制代码
2026-06-26|2026-06-26 11:24:33|2026年06月26日|176.142054201569
函数 说明
date('now') 当前 UTC 日期
time('now') 当前 UTC 时间
datetime('now') 当前 UTC 日期时间
datetime('now','localtime') 转本地时间
strftime(fmt, timestr) 格式化时间
julianday(timestr) 转儒略日数(可做日期差值)

常用修饰符:

sql 复制代码
SELECT
    datetime('now', '+1 day'),        -- 明天
    datetime('now', '-7 days'),       -- 7天前
    datetime('now', 'start of month'),-- 本月第一天
    datetime('now', '+1 month');      -- 下个月今天

二十、常用函数速查

20.1 字符串函数

sql 复制代码
SELECT
    upper('hello'),          -- HELLO
    lower('WORLD'),          -- world
    length('SQLite'),        -- 6
    substr('SQLite', 3, 3),  -- Lite
    replace('hi,world', ',', ' '), -- hi world
    trim('  hello  '),       -- hello
    printf('%.2f', 3.14159); -- 3.14

20.2 数值函数

sql 复制代码
SELECT
    abs(-42),        -- 42
    round(3.567, 2), -- 3.57
    max(1, 2, 3),    -- 3
    min(1, 2, 3);    -- 1

实战演示:

sql 复制代码
SELECT name, score,
       round(score, 0)  AS 四舍五入,
       abs(score - 85)  AS 与85距离
FROM students ORDER BY score DESC LIMIT 4;
复制代码
王芳|96.0|96.0|11.0
张伟|92.5|93.0|7.5
赵磊|91.0|91.0|6.0
陈静|88.5|89.0|3.5

20.3 聚合函数

sql 复制代码
SELECT
    count(*),          -- 总行数
    count(email),      -- 非 NULL 行数
    sum(score),        -- 求和
    avg(score),        -- 平均
    max(score),        -- 最大
    min(score),        -- 最小
    group_concat(name, ', ')  -- 字符串聚合
FROM students;

二十一、AUTOINCREMENT

sql 复制代码
CREATE TABLE t (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    val TEXT
);

INSERT INTO t (val) VALUES ('a'), ('b'), ('c');
DELETE FROM t WHERE val = 'c';  -- 删除 id=3

-- 无 AUTOINCREMENT:下一个 id 可能复用 3
-- 有 AUTOINCREMENT:严格递增,下一个是 4

查看当前序列值:

sql 复制代码
SELECT seq FROM sqlite_sequence WHERE name = 'students';
-- 实机输出:11 (历史最大 id)

二十二、VACUUM --- 数据库整理

sql 复制代码
-- 删除大量数据后,数据库文件不会自动缩小
-- VACUUM 整理碎片,回收空间(需额外磁盘临时空间)
VACUUM;

-- 在线 VACUUM(不阻塞读操作,WAL 模式下)
PRAGMA auto_vacuum = INCREMENTAL;
PRAGMA incremental_vacuum(100);

二十三、SQLite 注入防护

危险写法(字符串拼接,存在注入风险):

python 复制代码
# ❌ 绝对不要这样写
name = "' OR '1'='1"
cursor.execute(f"SELECT * FROM students WHERE name = '{name}'")
# 实际执行:SELECT * FROM students WHERE name = '' OR '1'='1'
# → 返回全表数据!

安全写法(参数化查询):

python 复制代码
# ✅ 使用占位符 ?
cursor.execute("SELECT * FROM students WHERE name = ?", (name,))

# ✅ 命名占位符
cursor.execute("SELECT * FROM students WHERE grade = :grade", {"grade": "A"})

二十四、Python 接口实战

Python 标准库 sqlite3 零依赖,直接使用:

python 复制代码
#!/usr/bin/env python3
import sqlite3
import os

DB_PATH = '/tmp/py_demo.db'

# ── 连接 & 建表 ──────────────────────────────────────
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row  # 支持用列名访问:row["name"]
cursor = conn.cursor()

cursor.execute('''
CREATE TABLE products (
    id    INTEGER PRIMARY KEY AUTOINCREMENT,
    name  TEXT    NOT NULL,
    price REAL    NOT NULL,
    stock INTEGER DEFAULT 0
)
''')

# ── 批量插入 ──────────────────────────────────────────
products = [
    ('苹果', 5.5, 100),
    ('香蕉', 3.0, 200),
    ('橙子', 4.8, 150),
    ('葡萄', 12.0, 80),
    ('西瓜', 2.5, 50),
]
cursor.executemany(
    'INSERT INTO products (name, price, stock) VALUES (?, ?, ?)',
    products
)
conn.commit()
print(f'[INSERT] 插入 {len(products)} 条记录')

实机输出

复制代码
[INSERT] 插入 5 条记录,最后 rowid=0
python 复制代码
# ── 条件查询 ─────────────────────────────────────────
cursor.execute('SELECT * FROM products WHERE price > ?', (4.0,))
rows = cursor.fetchall()
print('[SELECT] 价格>4元 的商品:')
for row in rows:
    print(f'  id={row["id"]}, name={row["name"]}, price={row["price"]}')
复制代码
[SELECT] 价格>4元 的商品:
  id=1, name=苹果, price=5.5, stock=100
  id=3, name=橙子, price=4.8, stock=150
  id=4, name=葡萄, price=12.0, stock=80
python 复制代码
# ── 事务:库存扣减 ────────────────────────────────────
try:
    conn.execute('BEGIN')
    conn.execute('UPDATE products SET stock = stock - 10 WHERE name = ?', ('苹果',))
    conn.execute('UPDATE products SET stock = stock - 5 WHERE name = ?', ('香蕉',))
    conn.commit()
    print('[TRANSACTION] 库存扣减成功')
except Exception as e:
    conn.rollback()
    print(f'[TRANSACTION] 回滚: {e}')
复制代码
[TRANSACTION] 库存扣减成功
python 复制代码
# ── 聚合查询 ─────────────────────────────────────────
cursor.execute(
    'SELECT COUNT(*) as cnt, AVG(price) as avg_price, SUM(stock) as total FROM products'
)
row = cursor.fetchone()
print(f'[AGG] 商品数={row["cnt"]}, 均价={row["avg_price"]:.2f}, 总库存={row["total"]}')
复制代码
[AGG] 商品数=5, 均价=5.56, 总库存=565
python 复制代码
# ── with 语句(自动提交/关闭)────────────────────────
with sqlite3.connect(DB_PATH) as con:
    con.row_factory = sqlite3.Row
    result = con.execute(
        'SELECT name, price FROM products ORDER BY price DESC LIMIT 3'
    ).fetchall()
    print('[WITH] 最贵前3:')
    for r in result:
        print(f'  {r["name"]}: ¥{r["price"]}')

conn.close()
复制代码
[WITH] 最贵前3:
  葡萄: ¥12.0
  苹果: ¥5.5
  橙子: ¥4.8
[DONE] Python sqlite3 演示完成

Python API 速查

API 说明
sqlite3.connect(path) 连接数据库(:memory: 为内存库)
conn.cursor() 创建游标
cursor.execute(sql, params) 执行单条语句
cursor.executemany(sql, data) 批量执行
cursor.fetchone() 取一行
cursor.fetchall() 取所有行
cursor.fetchmany(n) 取 n 行
conn.commit() 提交事务
conn.rollback() 回滚事务
cursor.rowcount 影响行数
cursor.lastrowid 最后插入 rowid
conn.row_factory = sqlite3.Row 支持列名访问

二十五、附加数据库(ATTACH/DETACH)

sql 复制代码
-- 附加第二个数据库,别名为 backup
ATTACH DATABASE '/tmp/backup.db' AS backup;

-- 跨库查询
SELECT m.name, b.score
FROM main.students m
JOIN backup.students_old b ON m.id = b.id;

-- 跨库复制表
CREATE TABLE backup.students_backup AS SELECT * FROM main.students;

-- 验证:实机输出为 8
SELECT COUNT(*) FROM backup.students_backup;

-- 分离
DETACH DATABASE backup;

SQLite 单次连接最多附加 10 个数据库。


二十六、Explain 执行计划

sql 复制代码
-- 完整字节码(用于深度调试)
EXPLAIN SELECT name FROM students WHERE score > 85;

-- 查询计划(可读性高,调优首选)
EXPLAIN QUERY PLAN SELECT name FROM students WHERE score > 85;

有索引时输出

复制代码
QUERY PLAN
`--SEARCH students USING INDEX idx_score (score>?)

无索引时输出

复制代码
QUERY PLAN
`--SCAN students

SCAN → 全表扫描(O(n)),需要考虑建索引。


二十七、生产实践建议

27.1 文件权限与备份

bash 复制代码
# 数据库文件权限
chmod 600 /path/to/app.db
chown appuser:appgroup /path/to/app.db

# 在线热备份(SQLite 3.27.0+)
sqlite3 source.db ".backup /backup/source_$(date +%Y%m%d).db"

# 或用 cp(先 VACUUM 确保数据落盘)
sqlite3 source.db "VACUUM INTO '/backup/source.db';"

27.2 WAL 模式提升并发

sql 复制代码
-- 开启 WAL(Write-Ahead Logging)
-- 允许一个写者与多个读者并发,不互相阻塞
PRAGMA journal_mode = WAL;
-- 返回:wal

PRAGMA synchronous = NORMAL;  -- WAL + NORMAL 是推荐组合

27.3 何时换成 MySQL/PostgreSQL

情况 建议
并发写请求 > 10/s 换 MySQL
数据量 > 1TB 换 PostgreSQL
需要多机复制/主从 换 MySQL/PG
网络访问数据库 换 C/S 架构数据库
嵌入式/单机/测试 SQLite 足够

总结

复制代码
SQLite 核心架构回顾:

  ┌─────────────────────────────────────┐
  │         SQL 接口层                   │
  │   Parser → Optimizer → Bytecode     │
  ├─────────────────────────────────────┤
  │         存储引擎层                   │
  │   B-Tree → Page Cache → OS I/O     │
  ├─────────────────────────────────────┤
  │         单文件数据库                  │
  │   ┌──────┬──────┬──────┬──────┐    │
  │   │ 页头 │ 数据 │ 索引 │ WAL  │    │
  │   └──────┴──────┴──────┴──────┘    │
  └─────────────────────────────────────┘

SQLite 的哲学是"简单即美"------零配置、零依赖、单文件,却支持完整的 SQL 特性。理解它的边界(不适合高并发写、不适合分布式),在合适的场景下它是最优解。


实验环境信息

  • 服务器:华为云 FlexusX ecs-9fdf-0001 (8vCPU/16GiB)
  • 系统:Ubuntu 24.04.4 LTS (kernel 6.8.0-106-generic)
  • SQLite 版本:3.45.1 (2024-01-30)
  • Python 版本:3.12.3
  • 数据库文件:/tmp/school.db (学生选课场景)