SQLite3 完整操作指南:从入门到精通
适用于嵌入式开发、物联网、移动应用及桌面软件开发
目录
- [SQLite3 概述](#SQLite3 概述)
- 安装与环境配置
- 数据库基础操作
- 表操作(DDL)
- 数据类型详解
- [CRUD 操作](#CRUD 操作)
- 高级查询
- 索引优化
- 视图
- 事务处理
- [PRAGMA 配置(嵌入式重点)](#PRAGMA 配置(嵌入式重点))
- [WAL 模式详解](#WAL 模式详解)
- [C/C++ API 编程(嵌入式核心)](#C/C++ API 编程(嵌入式核心))
- [Python 操作 SQLite3](#Python 操作 SQLite3)
- 触发器
- 存储过程替代方案
- [JSON 支持](#JSON 支持)
- 全文搜索(FTS5)
- 备份与恢复
- 性能优化(嵌入式重点)
- 安全注意事项
- 嵌入式行业实战案例
- 常见问题与解决方案
1. SQLite3 概述
1.1 什么是 SQLite3
SQLite 是一个轻量级、嵌入式、零配置的关系型数据库引擎。它不是一个独立的数据库服务器,而是一个嵌入到应用程序中的库。
核心特点:
| 特性 | 说明 |
|---|---|
| 零配置 | 无需安装、无需管理、无需服务器进程 |
| 单文件存储 | 整个数据库存储在单个 .db 文件中 |
| 跨平台 | 支持 Linux、Windows、macOS、RTOS 等 |
| 体积小 | 编译后仅约 600KB |
| 事务支持 | 完整的 ACID 事务 |
| 公有领域 | 完全开源,无许可证限制 |
1.2 嵌入式行业应用场景
┌─────────────────────────────────────────────────┐
│ SQLite3 应用场景 │
├─────────────────────────────────────────────────┤
│ ● IoT 设备数据存储(传感器数据、配置信息) │
│ ● 智能家居控制系统的本地数据库 │
│ ● 工业控制设备的日志和参数存储 │
│ ● 车载信息娱乐系统 │
│ ● 医疗设备的数据记录 │
│ ● Android/iOS 应用本地存储 │
│ ● 嵌入式 Linux 系统的配置管理 │
│ ● 边缘计算节点的缓存数据库 │
└─────────────────────────────────────────────────┘
1.3 SQLite3 与其他数据库对比
| 特性 | SQLite3 | MySQL | PostgreSQL |
|---|---|---|---|
| 安装复杂度 | 零配置 | 需要安装 | 需要安装 |
| 运行方式 | 嵌入式 | C/S 架构 | C/S 架构 |
| 内存占用 | ~600KB | ~200MB+ | ~300MB+ |
| 并发写入 | 单写多读 | 支持 | 支持 |
| 网络访问 | 不支持 | 支持 | 支持 |
| 适用场景 | 嵌入式/移动 | Web/企业 | Web/企业 |
| 存储限制 | 281TB | 无限制 | 无限制 |
2. 安装与环境配置
2.1 Linux 安装
bash
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install sqlite3 libsqlite3-dev
# CentOS/RHEL
sudo yum install sqlite sqlite-devel
# Arch Linux
sudo pacman -S sqlite
# 验证安装
sqlite3 --version
# 输出示例:3.45.1 2024-01-30 16:01:20
2.2 Windows 安装
powershell
# 方法1:使用 scoop
scoop install sqlite
# 方法2:使用 chocolatey
choco install sqlite
# 方法3:手动下载
# 访问 https://www.sqlite.org/download.html
# 下载 sqlite-tools-win-x64-*.zip
# 解压后将目录添加到 PATH 环境变量
# 验证安装
sqlite3 --version
2.3 嵌入式交叉编译
bash
# 下载源码
wget https://www.sqlite.org/2024/sqlite-autoconf-3450100.tar.gz
tar xzf sqlite-autoconf-3450100.tar.gz
cd sqlite-autoconf-3450100
# 交叉编译(以 ARM 为例)
export CC=arm-linux-gnueabihf-gcc
export CXX=arm-linux-gnueabihf-g++
./configure --host=arm-linux-gnueabihf \
--prefix=/opt/sqlite3-arm \
--enable-static \
--disable-shared \
CFLAGS="-Os -DSQLITE_THREADSAFE=1"
make -j$(nproc)
make install
# 编译后库文件大小约 600KB(静态库约 1.2MB)
ls -lh /opt/sqlite3-arm/lib/
2.4 嵌入式裁剪编译(最小化体积)
bash
# 针对资源受限的 MCU/MPU,使用裁剪编译
./configure --host=arm-none-eabi \
CFLAGS=" \
-Os \
-DSQLITE_THREADSAFE=0 \
-DSQLITE_OMIT_PROGRESS_CALLBACK \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_AUTOVACUUM \
-DSQLITE_OMIT_DEPRECATED \
-DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_DEFAULT_MEMSTATUS=0 \
-DSQLITE_OMIT_UTF16 \
-DSQLITE_OMIT_TRACE \
-DSQLITE_OMIT_COMPLETE \
"
make
# 裁剪后可缩小到约 200-300KB
3. 数据库基础操作
3.1 创建/打开数据库
bash
# 方法1:命令行创建
sqlite3 mydb.db
# 方法2:如果文件不存在会自动创建
sqlite3 /path/to/mydb.db
# 方法3:使用内存数据库(临时数据,关闭后丢失)
sqlite3 :memory:
# 方法4:只读模式打开
sqlite3 "file:mydb.db?mode=ro" -uri
3.2 命令行常用命令
bash
# 进入 sqlite3 命令行后,所有以 . 开头的是元命令
# 显示所有表
.tables
# 显示表结构
.schema tablename
# 显示所有表的完整建表语句
.schema --indent
# 设置输出模式
.mode column # 列对齐模式
.mode csv # CSV 格式
.mode json # JSON 格式
.mode line # 每个字段一行
.mode markdown # Markdown 表格格式
.mode table # 表格边框模式
.mode tabs # Tab 分隔
# 设置输出文件(导出到文件)
.output result.txt # 输出到文件
.output stdout # 恢复输出到屏幕
# 显示列名
.headers on
# 设置 NULL 值显示
.nullvalue [NULL]
# 显示执行时间
.timer on
# 导出数据库为 SQL 文件
.dump > backup.sql
# 导入 SQL 文件
.read backup.sql
# 退出
.quit
.exit
3.3 命令行实用技巧
bash
# 一条命令执行 SQL(无需进入交互模式)
sqlite3 mydb.db "SELECT * FROM users;"
# 从文件执行 SQL
sqlite3 mydb.db < script.sql
# 带分隔符的导入
sqlite3 mydb.db
.mode csv
.import data.csv tablename
# 导出为 CSV
sqlite3 mydb.db
.headers on
.mode csv
.output data.csv
SELECT * FROM tablename;
.output stdout
4. 表操作(DDL)
4.1 创建表
sql
-- 基本建表语法
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
email TEXT,
age INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 嵌入式场景:传感器数据表
CREATE TABLE sensor_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sensor_id TEXT NOT NULL,
temperature REAL,
humidity REAL,
pressure REAL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
device_ip TEXT
);
-- 嵌入式场景:设备配置表
CREATE TABLE device_config (
config_key TEXT PRIMARY KEY,
config_value TEXT NOT NULL,
value_type TEXT DEFAULT 'string', -- string, int, float, bool, json
description TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 嵌入式场景:系统日志表
CREATE TABLE system_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
log_level TEXT NOT NULL, -- DEBUG, INFO, WARN, ERROR, FATAL
module TEXT NOT NULL,
message TEXT NOT NULL,
details TEXT, -- JSON 格式的详细信息
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 创建临时表(会话结束自动删除)
CREATE TEMP TABLE temp_results (
id INTEGER PRIMARY KEY,
value REAL
);
-- 使用 IF NOT EXISTS 避免重复创建错误
CREATE TABLE IF NOT EXISTS app_settings (
key TEXT PRIMARY KEY,
value TEXT
);
4.2 修改表结构
sql
-- 添加列
ALTER TABLE users ADD COLUMN phone TEXT;
ALTER TABLE sensor_data ADD COLUMN battery_level REAL DEFAULT 100.0;
-- 重命名表
ALTER TABLE users RENAME TO customers;
-- 重命名列(SQLite 3.25.0+)
ALTER TABLE customers RENAME COLUMN username TO user_name;
-- 注意:SQLite 不支持以下操作(需要重建表)
-- ALTER TABLE ... DROP COLUMN (SQLite 3.35.0+ 才支持)
-- ALTER TABLE ... MODIFY COLUMN
-- ALTER TABLE ... ADD CONSTRAINT
-- SQLite 3.35.0+ 支持 DROP COLUMN
ALTER TABLE customers DROP COLUMN email;
4.3 重建表(修改不支持的结构)
sql
-- 场景:需要删除某列(SQLite 3.35.0 之前的方法)
-- 步骤1:创建新表
CREATE TABLE users_new (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
age INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 步骤2:复制数据(排除不需要的列)
INSERT INTO users_new (id, username, age, created_at)
SELECT id, username, age, created_at FROM users;
-- 步骤3:删除旧表
DROP TABLE users;
-- 步骤4:重命名新表
ALTER TABLE users_new RENAME TO users;
4.4 删除表
sql
-- 删除表(如果存在)
DROP TABLE IF EXISTS temp_data;
-- 删除表中所有数据(保留结构)
DELETE FROM sensor_data;
-- 或者更快的方式(不触发触发器)
DELETE FROM sensor_data WHERE 1=1;
-- 清空表并重置自增 ID
DELETE FROM sqlite_sequence WHERE name='sensor_data';
5. 数据类型详解
5.1 SQLite3 的5种存储类型
SQLite 使用动态类型系统,每个值都有以下5种存储类型之一:
| 存储类型 | 说明 | 示例 |
|---|---|---|
| NULL | 空值 | NULL |
| INTEGER | 有符号整数(1/2/3/4/6/8字节) | 42, -1, 0 |
| REAL | 浮点数(8字节 IEEE 754) | 3.14, -0.5 |
| TEXT | 文本字符串(UTF-8/UTF-16) | "hello", 'world' |
| BLOB | 二进制大对象 | x'89504E47' |
5.2 类型亲和性(Type Affinity)
sql
-- SQLite 使用"类型亲和性"规则
-- 建表时声明的类型会被映射到5种存储类型之一
-- 以下声明最终都存储为 INTEGER
CREATE TABLE type_demo (
col1 INT, -- INTEGER 亲和性
col2 INTEGER, -- INTEGER 亲和性
col3 TINYINT, -- INTEGER 亲和性
col4 BIGINT, -- INTEGER 亲和性
col5 BOOLEAN, -- INTEGER 亲和性 (0=false, 1=true)
col6 TIMESTAMP, -- INTEGER 亲和性
col7 NUMERIC, -- NUMERIC 亲和性
col8 FLOAT, -- REAL 亲和性
col9 DOUBLE, -- REAL 亲和性
col10 VARCHAR(100), -- TEXT 亲和性
col11 CLOB, -- TEXT 亲和性
col12 BLOB -- BLOB 亲和性(无亲和性)
);
-- 类型亲和性规则:
-- 1. 包含 "INT" → INTEGER
-- 2. 包含 "CHAR", "CLOB", "TEXT" → TEXT
-- 3. 包含 "BLOB" → BLOB(无亲和性)
-- 4. 包含 "REAL", "FLOA", "DOUB" → REAL
-- 5. 其他 → NUMERIC
5.3 嵌入式场景的数据类型选择建议
sql
-- 传感器数据存储推荐
CREATE TABLE sensor_readings (
-- 主键:使用 INTEGER 而非 UUID,节省空间且更快
id INTEGER PRIMARY KEY AUTOINCREMENT,
-- 设备标识:TEXT(因为可能包含字母和符号)
device_id TEXT NOT NULL,
-- 传感器值:REAL(浮点数)
temperature REAL NOT NULL,
humidity REAL NOT NULL,
pressure REAL NOT NULL,
-- 时间戳:存储为 Unix 时间戳(INTEGER),比 DATETIME 更节省空间
timestamp INTEGER NOT NULL,
-- 状态标志:INTEGER(0/1 表示布尔值)
is_valid INTEGER DEFAULT 1,
-- 原始数据:BLOB(二进制数据)
raw_data BLOB,
-- 备注信息:TEXT
notes TEXT
);
-- 插入 Unix 时间戳
INSERT INTO sensor_readings (device_id, temperature, humidity, pressure, timestamp)
VALUES ('SENSOR_001', 25.5, 60.2, 1013.25, strftime('%s', 'now'));
-- 查询时转换为可读时间
SELECT
device_id,
temperature,
datetime(timestamp, 'unixepoch', 'localtime') as readable_time
FROM sensor_readings;
6. CRUD 操作
6.1 INSERT(插入数据)
sql
-- 基本插入
INSERT INTO users (username, email, age) VALUES ('张三', 'zhangsan@example.com', 25);
-- 插入多行
INSERT INTO users (username, email, age) VALUES
('李四', 'lisi@example.com', 30),
('王五', 'wangwu@example.com', 28),
('赵六', 'zhaoliu@example.com', 35);
-- 插入所有列(必须按建表顺序提供值)
INSERT INTO users VALUES (NULL, '钱七', 'qianqi@example.com', 22, CURRENT_TIMESTAMP);
-- 插入或忽略(遇到主键/唯一冲突时跳过)
INSERT OR IGNORE INTO users (username, email, age) VALUES ('张三', 'new@example.com', 26);
-- 插入或替换(遇到主键/唯一冲突时替换)
INSERT OR REPLACE INTO users (username, email, age) VALUES ('张三', 'updated@example.com', 27);
-- 从查询结果插入
INSERT INTO user_backup (username, email)
SELECT username, email FROM users WHERE age > 30;
-- 使用默认值
INSERT INTO users (username) VALUES ('新用户');
-- email=NULL, age=0, created_at=CURRENT_TIMESTAMP
-- 嵌入式场景:批量插入传感器数据
INSERT INTO sensor_data (sensor_id, temperature, humidity, pressure) VALUES
('TEMP_001', 25.5, 60.2, 1013.25),
('TEMP_002', 26.1, 58.7, 1013.30),
('HUM_001', 24.8, 65.3, 1013.20),
('PRESS_001', 25.0, 61.0, 1013.28);
6.2 SELECT(查询数据)
sql
-- 查询所有列
SELECT * FROM users;
-- 查询指定列
SELECT username, email FROM users;
-- 条件查询
SELECT * FROM users WHERE age > 25;
SELECT * FROM users WHERE username = '张三';
SELECT * FROM users WHERE age BETWEEN 20 AND 30;
SELECT * FROM users WHERE username IN ('张三', '李四', '王五');
SELECT * FROM users WHERE email IS NOT NULL;
SELECT * FROM users WHERE username LIKE '%张%'; -- 模糊查询
SELECT * FROM users WHERE username LIKE '张_'; -- 单字符匹配
-- 排序
SELECT * FROM users ORDER BY age ASC; -- 升序
SELECT * FROM users ORDER BY age DESC; -- 降序
SELECT * FROM users ORDER BY age DESC, id ASC; -- 多列排序
-- 分页查询(嵌入式常用,避免一次加载过多数据)
SELECT * FROM users LIMIT 10; -- 前10条
SELECT * FROM users LIMIT 10 OFFSET 20; -- 跳过20条,取10条
SELECT * FROM users LIMIT 10, 20; -- 同上(简写形式)
-- 去重查询
SELECT DISTINCT age FROM users;
SELECT DISTINCT age, city FROM users;
-- 别名
SELECT username AS 用户名, age AS 年龄 FROM users;
-- 条件表达式
SELECT
username,
age,
CASE
WHEN age < 18 THEN '未成年'
WHEN age BETWEEN 18 AND 60 THEN '成年人'
ELSE '老年人'
END AS age_group
FROM users;
6.3 UPDATE(更新数据)
sql
-- 基本更新
UPDATE users SET email = 'new@example.com' WHERE username = '张三';
-- 更新多个字段
UPDATE users SET email = 'new@example.com', age = 26 WHERE id = 1;
-- 使用表达式更新
UPDATE products SET price = price * 1.1; -- 所有商品涨价10%
UPDATE sensor_data SET temperature = temperature + 0.5 WHERE sensor_id = 'TEMP_001';
-- 使用子查询更新
UPDATE users SET age = (SELECT AVG(age) FROM users) WHERE age IS NULL;
-- 更新或忽略(遇到约束冲突时跳过)
UPDATE OR IGNORE users SET username = '张三' WHERE id = 2;
-- 嵌入式场景:更新设备在线状态
UPDATE devices SET
last_seen = strftime('%s', 'now'),
is_online = 1,
ip_address = '192.168.1.100'
WHERE device_id = 'DEV_001';
-- 注意:始终使用 WHERE 子句!没有 WHERE 会更新所有行!
6.4 DELETE(删除数据)
sql
-- 基本删除
DELETE FROM users WHERE id = 1;
-- 条件删除
DELETE FROM users WHERE age < 18;
DELETE FROM sensor_data WHERE timestamp < strftime('%s', '2024-01-01');
-- 删除所有数据
DELETE FROM temp_data;
-- 嵌入式场景:清理过期日志(保留最近7天)
DELETE FROM system_logs
WHERE created_at < datetime('now', '-7 days');
-- 嵌入式场景:清理过期传感器数据(保留最近30天)
DELETE FROM sensor_data
WHERE timestamp < strftime('%s', 'now', '-30 days');
-- 使用 LIMIT 限制删除数量(分批删除,避免长时间锁表)
DELETE FROM sensor_data
WHERE id IN (
SELECT id FROM sensor_data
WHERE timestamp < strftime('%s', 'now', '-90 days')
LIMIT 1000
);
-- 注意:始终使用 WHERE 子句!
6.5 UPSERT(插入或更新,SQLite 3.24.0+)
sql
-- 基本 UPSERT 语法
INSERT INTO users (username, email, age)
VALUES ('张三', 'new@example.com', 26)
ON CONFLICT(username) DO UPDATE SET
email = excluded.email,
age = excluded.age;
-- 嵌入式场景:更新设备配置(存在则更新,不存在则插入)
INSERT INTO device_config (config_key, config_value)
VALUES ('wifi_ssid', 'MyNetwork')
ON CONFLICT(config_key) DO UPDATE SET
config_value = excluded.config_value,
updated_at = CURRENT_TIMESTAMP;
-- 嵌入式场景:更新传感器最新读数
INSERT INTO sensor_latest (sensor_id, value, timestamp)
VALUES ('TEMP_001', 25.5, strftime('%s', 'now'))
ON CONFLICT(sensor_id) DO UPDATE SET
value = excluded.value,
timestamp = excluded.timestamp;
-- 忽略冲突(不更新,直接跳过)
INSERT OR IGNORE INTO users (username, email) VALUES ('张三', 'duplicate@example.com');
7. 高级查询
7.1 聚合函数
sql
-- 常用聚合函数
SELECT COUNT(*) FROM users; -- 总行数
SELECT COUNT(email) FROM users; -- 非 NULL 的 email 数量
SELECT COUNT(DISTINCT age) FROM users; -- 不同年龄的数量
SELECT SUM(age) FROM users; -- 年龄总和
SELECT AVG(age) FROM users; -- 平均年龄
SELECT MIN(age) FROM users; -- 最小年龄
SELECT MAX(age) FROM users; -- 最大年龄
SELECT GROUP_CONCAT(username, ', ') FROM users; -- 连接所有用户名
-- GROUP BY 分组统计
SELECT age, COUNT(*) as count FROM users GROUP BY age;
SELECT city, AVG(age) as avg_age FROM users GROUP BY city;
-- HAVING 过滤分组结果
SELECT age, COUNT(*) as count
FROM users
GROUP BY age
HAVING count > 2;
-- 嵌入式场景:按设备统计传感器数据
SELECT
sensor_id,
COUNT(*) as reading_count,
AVG(temperature) as avg_temp,
MIN(temperature) as min_temp,
MAX(temperature) as max_temp,
AVG(humidity) as avg_humidity
FROM sensor_data
WHERE timestamp > strftime('%s', 'now', '-24 hours')
GROUP BY sensor_id;
-- 嵌入式场景:按天统计日志数量
SELECT
date(created_at) as log_date,
log_level,
COUNT(*) as count
FROM system_logs
GROUP BY log_date, log_level
ORDER BY log_date DESC;
7.2 JOIN(连接查询)
sql
-- 创建示例表
CREATE TABLE departments (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
dept_id INTEGER,
FOREIGN KEY (dept_id) REFERENCES departments(id)
);
INSERT INTO departments VALUES (1, '研发部'), (2, '市场部'), (3, '财务部');
INSERT INTO employees VALUES
(1, '张三', 1), (2, '李四', 1), (3, '王五', 2),
(4, '赵六', 2), (5, '钱七', NULL);
-- INNER JOIN(内连接):只返回两表都匹配的行
SELECT e.name, d.name as department
FROM employees e
INNER JOIN departments d ON e.dept_id = d.id;
-- 结果:张三-研发部, 李四-研发部, 王五-市场部, 赵六-市场部
-- LEFT JOIN(左连接):返回左表所有行,右表匹配的行
SELECT e.name, d.name as department
FROM employees e
LEFT JOIN departments d ON e.dept_id = d.id;
-- 结果:包含钱七(department 为 NULL)
-- RIGHT JOIN(右连接):SQLite 3.39.0+ 支持
SELECT e.name, d.name as department
FROM employees e
RIGHT JOIN departments d ON e.dept_id = d.id;
-- CROSS JOIN(交叉连接/笛卡尔积)
SELECT e.name, d.name FROM employees e CROSS JOIN departments d;
-- 自连接
-- 查找同部门的员工对
SELECT e1.name as employee1, e2.name as employee2
FROM employees e1
JOIN employees e2 ON e1.dept_id = e2.dept_id AND e1.id < e2.id;
-- 嵌入式场景:关联设备和传感器数据
SELECT
d.device_name,
d.location,
s.temperature,
s.humidity,
datetime(s.timestamp, 'unixepoch') as reading_time
FROM devices d
INNER JOIN sensor_data s ON d.device_id = s.sensor_id
WHERE s.timestamp > strftime('%s', 'now', '-1 hour')
ORDER BY s.timestamp DESC;
7.3 子查询
sql
-- 标量子查询(返回单个值)
SELECT * FROM users WHERE age > (SELECT AVG(age) FROM users);
-- 列子查询(返回一列)
SELECT * FROM users WHERE dept_id IN (SELECT id FROM departments WHERE name LIKE '%技术%');
-- 行子查询
SELECT * FROM users WHERE (age, city) = (SELECT age, city FROM users WHERE id = 1);
-- EXISTS 子查询
SELECT * FROM departments d
WHERE EXISTS (SELECT 1 FROM employees e WHERE e.dept_id = d.id);
-- NOT EXISTS
SELECT * FROM departments d
WHERE NOT EXISTS (SELECT 1 FROM employees e WHERE e.dept_id = d.id);
-- 派生表(FROM 子查询)
SELECT avg_by_city.city, avg_by_city.avg_age
FROM (
SELECT city, AVG(age) as avg_age
FROM users
GROUP BY city
) as avg_by_city
WHERE avg_by_city.avg_age > 25;
-- 嵌入式场景:查找最近一次温度超过阈值的传感器
SELECT sensor_id, temperature, timestamp
FROM sensor_data
WHERE temperature > 30.0
AND timestamp = (
SELECT MAX(timestamp) FROM sensor_data s2
WHERE s2.sensor_id = sensor_data.sensor_id
);
7.4 窗口函数(SQLite 3.25.0+)
sql
-- ROW_NUMBER():行号
SELECT
name,
age,
ROW_NUMBER() OVER (ORDER BY age DESC) as rank
FROM users;
-- RANK():排名(有并列)
SELECT
name,
age,
RANK() OVER (ORDER BY age DESC) as rank
FROM users;
-- DENSE_RANK():密集排名
SELECT
name,
age,
DENSE_RANK() OVER (ORDER BY age DESC) as rank
FROM users;
-- 分区窗口函数
SELECT
name,
city,
age,
ROW_NUMBER() OVER (PARTITION BY city ORDER BY age DESC) as city_rank
FROM users;
-- 累计求和
SELECT
date,
amount,
SUM(amount) OVER (ORDER BY date) as running_total
FROM transactions;
-- 移动平均
SELECT
timestamp,
temperature,
AVG(temperature) OVER (
ORDER BY timestamp
ROWS BETWEEN 4 PRECEDING AND CURRENT ROW
) as moving_avg_5
FROM sensor_data;
-- LAG/LEAD:访问前后行
SELECT
sensor_id,
temperature,
timestamp,
LAG(temperature, 1) OVER (PARTITION BY sensor_id ORDER BY timestamp) as prev_temp,
temperature - LAG(temperature, 1) OVER (PARTITION BY sensor_id ORDER BY timestamp) as temp_change
FROM sensor_data;
-- 嵌入式场景:每个传感器的最新读数排名
SELECT
sensor_id,
temperature,
timestamp,
ROW_NUMBER() OVER (PARTITION BY sensor_id ORDER BY timestamp DESC) as recency
FROM sensor_data
HAVING recency = 1; -- 取每个传感器最新的一条
7.5 CTE(公用表表达式)
sql
-- 基本 CTE
WITH active_users AS (
SELECT * FROM users WHERE status = 'active'
)
SELECT * FROM active_users WHERE age > 25;
-- 多个 CTE
WITH
dept_count AS (
SELECT dept_id, COUNT(*) as emp_count
FROM employees GROUP BY dept_id
),
avg_salary AS (
SELECT dept_id, AVG(salary) as avg_sal
FROM employees GROUP BY dept_id
)
SELECT d.name, dc.emp_count, a.avg_sal
FROM departments d
JOIN dept_count dc ON d.id = dc.dept_id
JOIN avg_salary a ON d.id = a.dept_id;
-- 递归 CTE(遍历树形结构)
CREATE TABLE categories (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
parent_id INTEGER
);
INSERT INTO categories VALUES
(1, '电子产品', NULL),
(2, '手机', 1),
(3, 'iPhone', 2),
(4, 'iPhone 15', 3),
(5, 'iPhone 15 Pro', 3),
(6, '笔记本', 1),
(7, 'MacBook', 6);
-- 查询某个分类及其所有子分类
WITH RECURSIVE category_tree AS (
-- 基础查询:起始节点
SELECT id, name, parent_id, 0 as depth, name as path
FROM categories WHERE id = 1
UNION ALL
-- 递归查询:子节点
SELECT c.id, c.name, c.parent_id, ct.depth + 1, ct.path || ' > ' || c.name
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree ORDER BY path;
-- 嵌入式场景:遍历设备层级结构
WITH RECURSIVE device_tree AS (
SELECT id, name, parent_id, 0 as level
FROM devices WHERE parent_id IS NULL
UNION ALL
SELECT d.id, d.name, d.parent_id, dt.level + 1
FROM devices d
JOIN device_tree dt ON d.parent_id = dt.id
)
SELECT
printf('%s%s', substr(' ', 1, level * 4), name) as tree_view
FROM device_tree;
7.6 集合操作
sql
-- UNION:合并结果集(去重)
SELECT name FROM employees WHERE dept_id = 1
UNION
SELECT name FROM employees WHERE dept_id = 2;
-- UNION ALL:合并结果集(保留重复)
SELECT name FROM employees WHERE dept_id = 1
UNION ALL
SELECT name FROM employees WHERE dept_id = 2;
-- INTERSECT:交集
SELECT name FROM employees WHERE dept_id = 1
INTERSECT
SELECT name FROM employees WHERE salary > 5000;
-- EXCEPT:差集
SELECT name FROM employees WHERE dept_id = 1
EXCEPT
SELECT name FROM employees WHERE salary < 5000;
8. 索引优化
8.1 创建索引
sql
-- 基本索引
CREATE INDEX idx_users_email ON users(email);
-- 唯一索引
CREATE UNIQUE INDEX idx_users_username ON users(username);
-- 多列索引(复合索引)
CREATE INDEX idx_sensor_time ON sensor_data(sensor_id, timestamp);
-- 部分索引(条件索引,节省空间)
CREATE INDEX idx_active_users ON users(email) WHERE status = 'active';
-- 覆盖索引(包含查询所需的所有列)
CREATE INDEX idx_sensor_covering ON sensor_data(sensor_id, timestamp, temperature, humidity);
-- 表达式索引
CREATE INDEX idx_users_lower_email ON users(lower(email));
-- 使用 IF NOT EXISTS
CREATE INDEX IF NOT EXISTS idx_users_age ON users(age);
8.2 索引使用策略
sql
-- 查看查询执行计划
EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = 'test@example.com';
-- 输出示例:
-- SEARCH TABLE users USING INDEX idx_users_email (email=?)
-- 查看详细执行计划
EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';
-- 索引使用原则:
-- 1. WHERE 子句中频繁使用的列
-- 2. JOIN 连接条件的列
-- 3. ORDER BY 排序的列
-- 4. GROUP BY 分组的列
-- 嵌入式场景:传感器数据查询优化
-- 为时间和传感器ID创建复合索引
CREATE INDEX idx_sensor_data_time_sensor
ON sensor_data(timestamp, sensor_id);
-- 查询最近1小时的温度数据(使用索引)
SELECT sensor_id, temperature, timestamp
FROM sensor_data
WHERE timestamp > strftime('%s', 'now', '-1 hours')
ORDER BY timestamp DESC;
-- 查看索引使用情况
SELECT * FROM sqlite_master WHERE type = 'index';
8.3 删除索引
sql
-- 删除索引
DROP INDEX idx_users_email;
-- 使用 IF EXISTS
DROP INDEX IF EXISTS idx_users_email;
-- 查看表的所有索引
.indices users
-- 或
SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name = 'users';
8.4 索引优化建议
sql
-- 避免在小表上创建索引(通常 < 1000 行不需要)
-- 避免在频繁更新的列上创建过多索引
-- 复合索引的列顺序很重要:
-- 把等值查询的列放在前面,范围查询的列放在后面
CREATE INDEX idx_good ON sensor_data(sensor_id, timestamp);
-- 以下查询可以使用该索引:
SELECT * FROM sensor_data WHERE sensor_id = 'S1' AND timestamp > 1000;
-- 以下查询不能高效使用该索引:
SELECT * FROM sensor_data WHERE timestamp > 1000; -- sensor_id 不在条件中
-- 使用 ANALYZE 收集统计信息,帮助优化器选择最佳索引
ANALYZE;
ANALYZE sensor_data; -- 只分析特定表
9. 视图
9.1 创建视图
sql
-- 基本视图
CREATE VIEW active_users AS
SELECT id, username, email, age
FROM users
WHERE status = 'active';
-- 使用视图
SELECT * FROM active_users WHERE age > 25;
-- 嵌入式场景:设备状态汇总视图
CREATE VIEW device_status_summary AS
SELECT
d.device_id,
d.device_name,
d.location,
s.temperature as latest_temp,
s.humidity as latest_humidity,
datetime(s.timestamp, 'unixepoch', 'localtime') as last_reading,
CASE
WHEN s.timestamp > strftime('%s', 'now', '-5 minutes') THEN '在线'
WHEN s.timestamp > strftime('%s', 'now', '-1 hour') THEN '可能离线'
ELSE '离线'
END as status
FROM devices d
LEFT JOIN sensor_latest s ON d.device_id = s.sensor_id;
-- 嵌入式场景:每小时传感器平均值视图
CREATE VIEW hourly_sensor_avg AS
SELECT
sensor_id,
datetime(timestamp, 'unixepoch', 'start of hour') as hour,
AVG(temperature) as avg_temp,
AVG(humidity) as avg_humidity,
COUNT(*) as reading_count
FROM sensor_data
GROUP BY sensor_id, hour;
9.2 修改和删除视图
sql
-- 修改视图(替换)
CREATE OR REPLACE VIEW active_users AS
SELECT id, username, email, age, created_at
FROM users
WHERE status = 'active';
-- 删除视图
DROP VIEW IF EXISTS active_users;
-- 查看所有视图
SELECT name FROM sqlite_master WHERE type = 'view';
9.3 可更新视图
sql
-- 简单视图是可更新的(满足条件时)
CREATE VIEW adult_users AS
SELECT id, username, email, age FROM users WHERE age >= 18;
-- 可以通过视图更新数据
UPDATE adult_users SET email = 'new@example.com' WHERE id = 1;
INSERT INTO adult_users (username, email, age) VALUES ('新用户', 'new@example.com', 20);
DELETE FROM adult_users WHERE id = 5;
-- 不可更新的视图(包含以下特性时):
-- - DISTINCT
-- - GROUP BY / HAVING
-- - 聚合函数
-- - UNION / INTERSECT / EXCEPT
-- - 子查询
-- - JOIN(某些情况)
10. 事务处理
10.1 基本事务
sql
-- 开始事务
BEGIN TRANSACTION;
-- 或简写
BEGIN;
-- 执行多个操作
INSERT INTO accounts (name, balance) VALUES ('Alice', 1000);
INSERT INTO accounts (name, balance) VALUES ('Bob', 500);
-- 转账操作(原子性)
UPDATE accounts SET balance = balance - 100 WHERE name = 'Alice';
UPDATE accounts SET balance = balance + 100 WHERE name = 'Bob';
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;
10.2 事务类型
sql
-- DEFERRED(默认):延迟获取锁
BEGIN DEFERRED TRANSACTION;
-- IMMEDIATE:立即获取共享锁
BEGIN IMMEDIATE TRANSACTION;
-- EXCLUSIVE:立即获取排他锁
BEGIN EXCLUSIVE TRANSACTION;
-- 嵌入式建议:
-- 读多写少:使用 DEFERRED
-- 写操作:使用 IMMEDIATE
-- 批量写入:使用 EXCLUSIVE
10.3 保存点(嵌套事务)
sql
-- 使用保存点实现部分回滚
BEGIN;
INSERT INTO orders (product, quantity) VALUES ('Widget', 10);
SAVEPOINT sp1;
INSERT INTO order_items (order_id, product, qty) VALUES (1, 'Part A', 5);
SAVEPOINT sp2;
INSERT INTO order_items (order_id, product, qty) VALUES (1, 'Part B', 3);
-- 发生错误,回滚到 sp2
ROLLBACK TO SAVEPOINT sp2;
-- 继续其他操作
INSERT INTO order_items (order_id, product, qty) VALUES (1, 'Part C', 2);
-- 释放保存点
RELEASE SAVEPOINT sp1;
COMMIT;
10.4 自动提交模式
sql
-- SQLite 默认每条语句都是一个单独的事务(自动提交)
-- 关闭自动提交(进入手动事务模式)
-- 在 C API 中:
-- sqlite3_exec(db, "BEGIN", 0, 0, 0);
-- ... 执行多个操作 ...
-- sqlite3_exec(db, "COMMIT", 0, 0, 0);
-- 嵌入式场景:批量插入优化
BEGIN;
INSERT INTO sensor_data VALUES (...);
INSERT INTO sensor_data VALUES (...);
-- ... 批量插入 ...
COMMIT;
-- 比逐条插入快 100 倍以上
10.5 事务与性能
sql
-- 性能对比:
-- 慢:每条 INSERT 单独一个事务(自动提交)
INSERT INTO sensor_data VALUES ('S1', 25.5, 60, 1013, 1000);
INSERT INTO sensor_data VALUES ('S2', 26.1, 58, 1013, 1001);
INSERT INTO sensor_data VALUES ('S3', 24.8, 65, 1013, 1002);
-- 每次都要 fsync,非常慢
-- 快:批量操作在一个事务中
BEGIN;
INSERT INTO sensor_data VALUES ('S1', 25.5, 60, 1013, 1000);
INSERT INTO sensor_data VALUES ('S2', 26.1, 58, 1013, 1001);
INSERT INTO sensor_data VALUES ('S3', 24.8, 65, 1013, 1002);
COMMIT;
-- 只需一次 fsync,快 100 倍以上
-- 嵌入式最佳实践:每 100-1000 条记录提交一次
11. PRAGMA 配置(嵌入式重点)
11.1 数据库信息查询
sql
-- 查看数据库文件列表
PRAGMA database_list;
-- 输出:seq | name | file
-- 0 | main | /path/to/mydb.db
-- 查看表信息
PRAGMA table_info(users);
-- 输出:cid | name | type | notnull | dflt_value | pk
-- 0 | id | INTEGER | 0 | NULL | 1
-- 1 | username | TEXT | 1 | NULL | 0
-- 查看表的索引
PRAGMA index_list(users);
PRAGMA index_info(idx_users_email);
-- 查看外键信息
PRAGMA foreign_key_list(employees);
-- 查看表的统计信息
PRAGMA stats;
11.2 性能相关 PRAGMA(嵌入式核心)
sql
-- 设置缓存大小(页数,负数表示 KB)
PRAGMA cache_size = -2000; -- 2MB 缓存
-- 默认值:-2000(2MB)
-- 嵌入式建议:根据可用内存调整
-- 设置页大小(必须在建表前设置)
PRAGMA page_size = 4096; -- 4KB(默认)
-- 可选:512, 1024, 2048, 4096, 8192, 16384, 32768, 65536
-- 嵌入式建议:与文件系统块大小匹配
-- 设置临时文件存储位置
PRAGMA temp_store = MEMORY; -- 临时表存储在内存中
-- 可选:DEFAULT(0), FILE(1), MEMORY(2)
-- 嵌入式建议:如果内存充足,设为 MEMORY
-- 设置 mmap 大小(内存映射 I/O)
PRAGMA mmap_size = 268435456; -- 256MB
-- 设为 0 禁用 mmap
-- 嵌入式建议:仅在支持 mmap 的系统上使用
-- 同步模式(影响性能和数据安全)
PRAGMA synchronous = FULL; -- 最安全,最慢
-- 可选:OFF(0), NORMAL(1), FULL(2), EXTRA(3)
-- 嵌入式建议:NORMAL(平衡性能和安全)
-- 页面缓存命中率统计
PRAGMA cache_spill = 0; -- 禁用缓存溢出到磁盘
11.3 安全相关 PRAGMA
sql
-- 外键约束(默认关闭!必须显式开启)
PRAGMA foreign_keys = ON;
-- 递归触发器
PRAGMA recursive_triggers = ON; -- 默认 OFF
-- 安全删除(删除时覆盖数据,防止数据恢复)
PRAGMA secure_delete = ON; -- 默认 OFF
-- 嵌入式敏感数据场景建议开启
-- 忽略检查约束
PRAGMA ignore_check_constraints = OFF; -- 默认 OFF
11.4 WAL 模式相关 PRAGMA
sql
-- 启用 WAL 模式(强烈推荐)
PRAGMA journal_mode = WAL;
-- WAL 自动检查点阈值(页数)
PRAGMA wal_autocheckpoint = 1000; -- 默认 1000
-- 查询当前 WAL 文件大小
PRAGMA wal_checkpoint;
-- WAL 检查点模式
PRAGMA wal_checkpoint(TRUNCATE); -- 截断 WAL 文件
PRAGMA wal_checkpoint(PASSIVE); -- 非阻塞检查点
PRAGMA wal_checkpoint(FULL); -- 完全检查点
PRAGMA wal_checkpoint(RESTART); -- 重启检查点
11.5 编码和兼容性
sql
-- 设置编码
PRAGMA encoding = 'UTF-8'; -- 默认
PRAGMA encoding = 'UTF-16';
PRAGMA encoding = 'UTF-16le';
PRAGMA encoding = 'UTF-16be';
-- SQLite 版本
PRAGMA compile_options; -- 编译选项
-- 设置应用 ID(识别数据库类型)
PRAGMA application_id = 12345678;
-- 用户版本(用于数据库迁移)
PRAGMA user_version = 1;
-- 嵌入式场景:用于版本控制和自动迁移
-- 应用启动时检查 user_version,执行必要的迁移
11.6 嵌入式 PRAGMA 配置模板
sql
-- 嵌入式系统启动时的推荐 PRAGMA 配置
-- 1. 启用 WAL 模式(提高并发性能)
PRAGMA journal_mode = WAL;
-- 2. 设置同步模式(平衡性能和安全)
PRAGMA synchronous = NORMAL;
-- 3. 设置缓存大小(根据可用内存调整)
PRAGMA cache_size = -4000; -- 4MB
-- 4. 临时数据存储在内存中
PRAGMA temp_store = MEMORY;
-- 5. 启用外键约束
PRAGMA foreign_keys = ON;
-- 6. 设置 mmap(如果系统支持)
PRAGMA mmap_size = 67108864; -- 64MB
-- 7. 设置页面大小(与文件系统块大小匹配)
PRAGMA page_size = 4096;
-- 8. 自动检查点
PRAGMA wal_autocheckpoint = 1000;
12. WAL 模式详解
12.1 WAL 模式概述
┌─────────────────────────────────────────────────────┐
│ 日志模式对比 │
├─────────────────────────────────────────────────────┤
│ DELETE 模式(默认): │
│ - 写入前将原始数据复制到日志文件 │
│ - 写入完成后删除日志文件 │
│ - 读写互斥 │
│ │
│ WAL 模式(推荐): │
│ - 写入追加到 WAL 文件末尾 │
│ - 读操作可以从原始数据库或 WAL 文件读取 │
│ - 支持并发读写 │
└─────────────────────────────────────────────────────┘
12.2 WAL 模式优势
┌─────────────────────────────────────────────────────┐
│ WAL 模式优势 │
├─────────────────────────────────────────────────────┤
│ ✅ 读写并发:多个读可以与一个写同时进行 │
│ ✅ 更好的写性能:顺序写入 WAL 文件,减少随机 I/O │
│ ✅ 更少的 fsync 调用:提高写入速度 │
│ ✅ 原子提交:崩溃恢复更可靠 │
│ ✅ 更好的并发性能:减少锁等待 │
└─────────────────────────────────────────────────────┘
12.3 启用和管理 WAL
sql
-- 启用 WAL 模式
PRAGMA journal_mode = WAL;
-- 验证是否启用成功
PRAGMA journal_mode;
-- 应该返回 "wal"
-- WAL 检查点(将 WAL 文件内容合并到主数据库)
PRAGMA wal_checkpoint; -- 被动检查点
PRAGMA wal_checkpoint(TRUNCATE); -- 截断 WAL 文件
-- 设置自动检查点阈值
PRAGMA wal_autocheckpoint = 1000; -- 默认 1000 页
-- 查看 WAL 文件状态
-- WAL 文件通常与数据库文件在同一目录
-- 文件名:database.db-wal
-- 文件名:database.db-shm(共享内存文件)
12.4 WAL 模式注意事项
sql
-- WAL 模式的限制:
-- 1. 不支持网络文件系统(NFS)上的并发写入
-- 2. 读取器必须在 WAL 文件中看到所有提交的事务
-- 3. WAL 文件会持续增长,直到检查点执行
-- 嵌入式场景:定期执行检查点
-- 方法1:在空闲时手动执行
PRAGMA wal_checkpoint(TRUNCATE);
-- 方法2:设置较小的自动检查点阈值
PRAGMA wal_autocheckpoint = 100; -- 100 页
-- 方法3:在应用层定期执行
-- 每 5 分钟执行一次检查点
-- 或者在每次写入大量数据后执行
12.5 WAL 模式性能调优
sql
-- 优化 WAL 写入性能
PRAGMA wal_autocheckpoint = 500; -- 更频繁的检查点
-- 调整页面大小(影响 WAL 文件大小)
PRAGMA page_size = 8192; -- 更大的页面
-- 使用 MEMORY 临时存储
PRAGMA temp_store = MEMORY;
-- 调整缓存大小
PRAGMA cache_size = -8000; -- 8MB 缓存
13. C/C++ API 编程(嵌入式核心)
13.1 基本操作
c
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
// 回调函数:处理查询结果
static int callback(void *data, int argc, char **argv, char **azColName) {
printf("%s: ", (const char*)data);
for (int i = 0; i < argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
int main() {
sqlite3 *db;
char *err_msg = NULL;
int rc;
// 1. 打开数据库
rc = sqlite3_open("sensor_data.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
// 2. 设置 PRAGMA(嵌入式优化)
sqlite3_exec(db, "PRAGMA journal_mode=WAL;", 0, 0, &err_msg);
sqlite3_exec(db, "PRAGMA synchronous=NORMAL;", 0, 0, &err_msg);
sqlite3_exec(db, "PRAGMA cache_size=-4000;", 0, 0, &err_msg);
sqlite3_exec(db, "PRAGMA temp_store=MEMORY;", 0, 0, &err_msg);
// 3. 创建表
const char *sql = "CREATE TABLE IF NOT EXISTS sensor_data ("
"id INTEGER PRIMARY KEY AUTOINCREMENT,"
"sensor_id TEXT NOT NULL,"
"temperature REAL,"
"humidity REAL,"
"timestamp INTEGER"
");";
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
if (rc != SQLITE_OK) {
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
}
// 4. 插入数据
sql = "INSERT INTO sensor_data (sensor_id, temperature, humidity, timestamp) "
"VALUES ('TEMP_001', 25.5, 60.2, strftime('%s', 'now'));";
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
// 5. 查询数据
const char* data = "Callback function called";
sql = "SELECT * FROM sensor_data;";
rc = sqlite3_exec(db, sql, callback, (void*)data, &err_msg);
// 6. 关闭数据库
sqlite3_close(db);
return 0;
}
13.2 预编译语句(Prepared Statements)
c
#include <stdio.h>
#include <stdlib.h>
#include <sqlite3.h>
int main() {
sqlite3 *db;
sqlite3_stmt *stmt;
int rc;
rc = sqlite3_open("sensor_data.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
return 1;
}
// 预编译 INSERT 语句
const char *insert_sql = "INSERT INTO sensor_data (sensor_id, temperature, humidity, timestamp) "
"VALUES (?, ?, ?, ?);";
rc = sqlite3_prepare_v2(db, insert_sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
fprintf(stderr, "Failed to prepare statement: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
// 批量插入(事务优化)
sqlite3_exec(db, "BEGIN TRANSACTION;", 0, 0, 0);
for (int i = 0; i < 1000; i++) {
// 绑定参数
sqlite3_bind_text(stmt, 1, "TEMP_001", -1, SQLITE_STATIC);
sqlite3_bind_double(stmt, 2, 25.0 + (rand() % 10) / 10.0);
sqlite3_bind_double(stmt, 3, 60.0 + (rand() % 20) / 10.0);
sqlite3_bind_int(stmt, 4, 1700000000 + i);
// 执行
rc = sqlite3_step(stmt);
if (rc != SQLITE_DONE) {
fprintf(stderr, "Insert failed: %s\n", sqlite3_errmsg(db));
}
// 重置语句(准备下一次执行)
sqlite3_reset(stmt);
sqlite3_clear_bindings(stmt);
}
sqlite3_exec(db, "COMMIT;", 0, 0, 0);
// 预编译 SELECT 语句
const char *select_sql = "SELECT sensor_id, temperature, humidity, timestamp "
"FROM sensor_data WHERE timestamp > ? ORDER BY timestamp;";
rc = sqlite3_prepare_v2(db, select_sql, -1, &stmt, NULL);
sqlite3_bind_int(stmt, 1, 1700000500);
// 遍历结果
while ((rc = sqlite3_step(stmt)) == SQLITE_ROW) {
const char *sensor_id = (const char*)sqlite3_column_text(stmt, 0);
double temperature = sqlite3_column_double(stmt, 1);
double humidity = sqlite3_column_double(stmt, 2);
int timestamp = sqlite3_column_int(stmt, 3);
printf("Sensor: %s, Temp: %.1f, Humidity: %.1f, Time: %d\n",
sensor_id, temperature, humidity, timestamp);
}
// 清理
sqlite3_finalize(stmt);
sqlite3_close(db);
return 0;
}
13.3 错误处理
c
#include <stdio.h>
#include <sqlite3.h>
// 错误处理宏
#define SQLITE_CHECK(db, rc, msg) \
do { \
if ((rc) != SQLITE_OK && (rc) != SQLITE_DONE && (rc) != SQLITE_ROW) { \
fprintf(stderr, "%s: %s (code: %d)\n", (msg), sqlite3_errmsg(db), (rc)); \
goto cleanup; \
} \
} while (0)
int main() {
sqlite3 *db = NULL;
sqlite3_stmt *stmt = NULL;
int rc;
// 打开数据库
rc = sqlite3_open_v2(
"test.db",
&db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX,
NULL
);
SQLITE_CHECK(db, rc, "Failed to open database");
// 设置忙等待回调(处理数据库锁定)
sqlite3_busy_handler(db, [](void *data, int retries) {
if (retries < 10) {
usleep(10000); // 等待 10ms
return 1; // 重试
}
return 0; // 放弃
}, NULL);
// 执行 SQL
rc = sqlite3_exec(db, "CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY);",
NULL, NULL, NULL);
SQLITE_CHECK(db, rc, "Failed to create table");
// 准备语句
rc = sqlite3_prepare_v2(db, "INSERT INTO test VALUES (?);", -1, &stmt, NULL);
SQLITE_CHECK(db, rc, "Failed to prepare statement");
// 绑定和执行
sqlite3_bind_int(stmt, 1, 42);
rc = sqlite3_step(stmt);
SQLITE_CHECK(db, rc, "Failed to execute statement");
cleanup:
// 清理资源
if (stmt) sqlite3_finalize(stmt);
if (db) sqlite3_close(db);
return (rc == SQLITE_OK || rc == SQLITE_DONE) ? 0 : 1;
}
13.4 线程安全
c
#include <stdio.h>
#include <pthread.h>
#include <sqlite3.h>
// 全局数据库连接(需要同步访问)
static sqlite3 *db;
// 互斥锁保护数据库访问
static pthread_mutex_t db_mutex = PTHREAD_MUTEX_INITIALIZER;
// 线程安全的数据库操作函数
int thread_safe_insert(const char *sensor_id, double temp, double humidity) {
static sqlite3_stmt *stmt = NULL;
int rc;
pthread_mutex_lock(&db_mutex);
// 首次调用时准备语句
if (stmt == NULL) {
const char *sql = "INSERT INTO sensor_data (sensor_id, temperature, humidity, timestamp) "
"VALUES (?, ?, ?, strftime('%s', 'now'));";
rc = sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
pthread_mutex_unlock(&db_mutex);
return -1;
}
}
sqlite3_bind_text(stmt, 1, sensor_id, -1, SQLITE_STATIC);
sqlite3_bind_double(stmt, 2, temp);
sqlite3_bind_double(stmt, 3, humidity);
rc = sqlite3_step(stmt);
sqlite3_reset(stmt);
sqlite3_clear_bindings(stmt);
pthread_mutex_unlock(&db_mutex);
return (rc == SQLITE_DONE) ? 0 : -1;
}
// 或者使用 SQLite 内置的线程安全模式
int init_thread_safe_db() {
// 检查 SQLite 编译时线程安全级别
int safety = sqlite3_threadsafe();
printf("Thread safety level: %d\n", safety);
// 0: 单线程
// 1: 多线程(多连接可以并发)
// 2: 序列化(完全线程安全)
// 使用 SQLITE_OPEN_FULLMUTEX 打开数据库
int rc = sqlite3_open_v2(
"sensor_data.db",
&db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX,
NULL
);
return rc;
}
13.5 内存数据库
c
#include <stdio.h>
#include <sqlite3.h>
// 使用内存数据库(临时数据,重启后丢失)
int use_memory_db() {
sqlite3 *db;
int rc;
// 打开内存数据库
rc = sqlite3_open(":memory:", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open memory database: %s\n", sqlite3_errmsg(db));
return -1;
}
// 内存数据库的特性:
// 1. 数据存储在内存中,速度极快
// 2. 连接关闭后数据丢失
// 3. 可以被多个连接共享(使用文件名 ":memory:")
// 4. 不会产生磁盘 I/O
// 使用场景:
// - 临时计算和缓存
// - 测试环境
// - 数据转换中间步骤
// - 嵌入式系统的临时数据
sqlite3_close(db);
return 0;
}
// 共享内存数据库(多个连接访问同一个内存数据库)
int shared_memory_db() {
sqlite3 *db1, *db2;
int rc;
// 使用 URI 格式创建共享内存数据库
rc = sqlite3_open("file::memory:?cache=shared", &db1);
rc = sqlite3_open("file::memory:?cache=shared", &db2);
// db1 和 db2 访问同一个内存数据库
sqlite3_exec(db1, "CREATE TABLE test (id INTEGER);", 0, 0, 0);
sqlite3_exec(db1, "INSERT INTO test VALUES (1);", 0, 0, 0);
// db2 可以读取 db1 创建的数据
sqlite3_exec(db2, "SELECT * FROM test;", callback, 0, 0);
sqlite3_close(db1);
sqlite3_close(db2);
return 0;
}
13.6 编译和链接
bash
# Linux/macOS
gcc -o myapp myapp.c -lsqlite3 -lpthread
# 嵌入式交叉编译
arm-linux-gnueabihf-gcc -o myapp myapp.c \
-I/opt/sqlite3-arm/include \
-L/opt/sqlite3-arm/lib \
-lsqlite3 -lpthread
# 使用 pkg-config
gcc -o myapp myapp.c $(pkg-config --cflags --libs sqlite3)
# 静态链接(嵌入式常用)
gcc -o myapp myapp.c \
/opt/sqlite3-arm/lib/libsqlite3.a \
-lpthread -ldl -lm
# 最小化编译(裁剪功能)
gcc -o myapp myapp.c \
-DSQLITE_THREADSAFE=0 \
-DSQLITE_OMIT_LOAD_EXTENSION \
-DSQLITE_OMIT_PROGRESS_CALLBACK \
libsqlite3.a
14. Python 操作 SQLite3
14.1 基本操作
python
import sqlite3
from datetime import datetime
# 连接数据库(文件不存在会自动创建)
conn = sqlite3.connect('sensor_data.db')
# 创建游标
cursor = conn.cursor()
# 设置 PRAGMA(嵌入式优化)
cursor.execute("PRAGMA journal_mode=WAL;")
cursor.execute("PRAGMA synchronous=NORMAL;")
cursor.execute("PRAGMA cache_size=-4000;")
# 创建表
cursor.execute('''
CREATE TABLE IF NOT EXISTS sensor_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sensor_id TEXT NOT NULL,
temperature REAL,
humidity REAL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
''')
# 插入数据
cursor.execute('''
INSERT INTO sensor_data (sensor_id, temperature, humidity)
VALUES (?, ?, ?)
''', ('TEMP_001', 25.5, 60.2))
# 提交事务
conn.commit()
# 查询数据
cursor.execute('SELECT * FROM sensor_data')
rows = cursor.fetchall()
for row in rows:
print(row)
# 关闭连接
conn.close()
14.2 使用上下文管理器
python
import sqlite3
# 使用 with 语句自动管理连接
with sqlite3.connect('sensor_data.db') as conn:
cursor = conn.cursor()
# 创建表
cursor.execute('''
CREATE TABLE IF NOT EXISTS sensor_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sensor_id TEXT NOT NULL,
temperature REAL,
humidity REAL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
''')
# 批量插入
data = [
('TEMP_001', 25.5, 60.2),
('TEMP_002', 26.1, 58.7),
('HUM_001', 24.8, 65.3),
]
cursor.executemany('''
INSERT INTO sensor_data (sensor_id, temperature, humidity)
VALUES (?, ?, ?)
''', data)
# 自动提交(with 语句结束时)
# 连接自动关闭
14.3 Row 工厂(字典访问)
python
import sqlite3
def dict_factory(cursor, row):
"""将查询结果转换为字典"""
d = {}
for idx, col in enumerate(cursor.description):
d[col[0]] = row[idx]
return d
conn = sqlite3.connect('sensor_data.db')
conn.row_factory = dict_factory
cursor = conn.cursor()
cursor.execute('SELECT * FROM sensor_data LIMIT 1')
row = cursor.fetchone()
# 现在可以通过列名访问
print(row['sensor_id']) # 而不是 row[1]
print(row['temperature']) # 而不是 row[2]
# 或者使用内置的 sqlite3.Row
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute('SELECT * FROM sensor_data LIMIT 1')
row = cursor.fetchone()
print(row['sensor_id']) # 可以通过列名访问
print(row['temperature'])
print(row.keys()) # 获取所有列名
14.4 异常处理
python
import sqlite3
try:
conn = sqlite3.connect('sensor_data.db')
cursor = conn.cursor()
cursor.execute('''
INSERT INTO sensor_data (sensor_id, temperature, humidity)
VALUES (?, ?, ?)
''', ('TEMP_001', 25.5, 60.2))
conn.commit()
except sqlite3.IntegrityError as e:
print(f"完整性错误: {e}")
conn.rollback()
except sqlite3.OperationalError as e:
print(f"操作错误: {e}")
conn.rollback()
except sqlite3.Error as e:
print(f"SQLite 错误: {e}")
conn.rollback()
finally:
if conn:
conn.close()
14.5 批量操作优化
python
import sqlite3
import time
def batch_insert_example():
conn = sqlite3.connect('sensor_data.db')
cursor = conn.cursor()
# 准备数据
data = [(f'SENSOR_{i:03d}', 20 + i * 0.1, 50 + i * 0.5)
for i in range(10000)]
# 方法1:逐条插入(慢)
start = time.time()
for row in data:
cursor.execute('''
INSERT INTO sensor_data (sensor_id, temperature, humidity)
VALUES (?, ?, ?)
''', row)
conn.commit()
print(f"逐条插入: {time.time() - start:.2f} 秒")
# 方法2:批量插入(快 100 倍)
start = time.time()
cursor.executemany('''
INSERT INTO sensor_data (sensor_id, temperature, humidity)
VALUES (?, ?, ?)
''', data)
conn.commit()
print(f"批量插入: {time.time() - start:.2f} 秒")
# 方法3:使用事务优化
start = time.time()
cursor.execute('BEGIN TRANSACTION')
for row in data:
cursor.execute('''
INSERT INTO sensor_data (sensor_id, temperature, humidity)
VALUES (?, ?, ?)
''', row)
cursor.execute('COMMIT')
print(f"事务优化: {time.time() - start:.2f} 秒")
conn.close()
batch_insert_example()
14.6 连接池(嵌入式服务)
python
import sqlite3
from contextlib import contextmanager
class SQLitePool:
"""简单的 SQLite 连接池"""
def __init__(self, database, max_connections=5):
self.database = database
self.max_connections = max_connections
self.connections = []
def get_connection(self):
if self.connections:
return self.connections.pop()
return sqlite3.connect(self.database)
def return_connection(self, conn):
if len(self.connections) < self.max_connections:
self.connections.append(conn)
else:
conn.close()
@contextmanager
def connection(self):
conn = self.get_connection()
try:
yield conn
conn.commit()
except Exception:
conn.rollback()
raise
finally:
self.return_connection(conn)
def close_all(self):
for conn in self.connections:
conn.close()
self.connections.clear()
# 使用连接池
pool = SQLitePool('sensor_data.db', max_connections=3)
with pool.connection() as conn:
cursor = conn.cursor()
cursor.execute('SELECT * FROM sensor_data')
print(cursor.fetchall())
pool.close_all()
14.7 数据库迁移工具
python
import sqlite3
class DatabaseMigrator:
"""简单的数据库迁移工具"""
def __init__(self, database):
self.database = database
self.conn = sqlite3.connect(database)
self.current_version = self.get_version()
def get_version(self):
cursor = self.conn.cursor()
cursor.execute("PRAGMA user_version")
return cursor.fetchone()[0]
def set_version(self, version):
self.conn.execute(f"PRAGMA user_version = {version}")
def migrate(self):
"""执行所有待执行的迁移"""
migrations = [
self.migration_v1,
self.migration_v2,
self.migration_v3,
]
for i, migration in enumerate(migrations, start=1):
if i > self.current_version:
print(f"执行迁移 v{i}...")
migration()
self.set_version(i)
self.conn.commit()
print(f"迁移 v{i} 完成")
def migration_v1(self):
"""创建基础表"""
self.conn.executescript('''
CREATE TABLE IF NOT EXISTS devices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT UNIQUE NOT NULL,
name TEXT,
location TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
''')
def migration_v2(self):
"""添加传感器数据表"""
self.conn.executescript('''
CREATE TABLE IF NOT EXISTS sensor_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT NOT NULL,
temperature REAL,
humidity REAL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (device_id) REFERENCES devices(device_id)
);
''')
def migration_v3(self):
"""添加索引"""
self.conn.executescript('''
CREATE INDEX IF NOT EXISTS idx_sensor_data_device
ON sensor_data(device_id, timestamp);
''')
# 使用迁移工具
migrator = DatabaseMigrator('embedded.db')
migrator.migrate()
15. 触发器
15.1 创建触发器
sql
-- 基本触发器语法
CREATE TRIGGER trigger_name
BEFORE|AFTER|INSTEAD OF INSERT|UPDATE|DELETE
ON table_name
[FOR EACH ROW]
[WHEN condition]
BEGIN
-- 触发器逻辑
END;
-- 示例:在插入用户前自动设置创建时间
CREATE TRIGGER set_user_created_at
BEFORE INSERT ON users
FOR EACH ROW
BEGIN
UPDATE users SET created_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
END;
-- 示例:在更新用户时记录修改历史
CREATE TRIGGER log_user_update
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
INSERT INTO user_audit_log (user_id, field, old_value, new_value, changed_at)
VALUES (OLD.id, 'email', OLD.email, NEW.email, CURRENT_TIMESTAMP);
END;
-- 示例:在删除用户时级联删除相关数据
CREATE TRIGGER cascade_delete_user_data
BEFORE DELETE ON users
FOR EACH ROW
BEGIN
DELETE FROM user_posts WHERE user_id = OLD.id;
DELETE FROM user_comments WHERE user_id = OLD.id;
DELETE FROM user_settings WHERE user_id = OLD.id;
END;
15.2 嵌入式场景触发器
sql
-- 场景1:自动清理过期数据
CREATE TRIGGER auto_cleanup_sensor_data
AFTER INSERT ON sensor_data
FOR EACH ROW
BEGIN
-- 每次插入新数据时,删除30天前的数据
DELETE FROM sensor_data
WHERE timestamp < strftime('%s', 'now', '-30 days')
AND id NOT IN (
SELECT id FROM sensor_data
WHERE timestamp < strftime('%s', 'now', '-30 days')
LIMIT 100
);
END;
-- 场景2:自动更新设备最后在线时间
CREATE TRIGGER update_device_last_seen
AFTER INSERT ON sensor_data
FOR EACH ROW
BEGIN
UPDATE devices
SET last_seen = CURRENT_TIMESTAMP,
is_online = 1
WHERE device_id = NEW.sensor_id;
END;
-- 场景3:自动计算统计数据
CREATE TRIGGER update_hourly_stats
AFTER INSERT ON sensor_data
FOR EACH ROW
BEGIN
INSERT OR REPLACE INTO hourly_stats (sensor_id, hour, avg_temp, avg_humidity, count)
SELECT
sensor_id,
strftime('%Y-%m-%d %H:00:00', timestamp, 'unixepoch'),
AVG(temperature),
AVG(humidity),
COUNT(*)
FROM sensor_data
WHERE sensor_id = NEW.sensor_id
AND strftime('%Y-%m-%d %H:00:00', timestamp, 'unixepoch') =
strftime('%Y-%m-%d %H:00:00', NEW.timestamp, 'unixepoch')
GROUP BY sensor_id, hour;
END;
-- 场景4:数据验证触发器
CREATE TRIGGER validate_sensor_data
BEFORE INSERT ON sensor_data
FOR EACH ROW
WHEN NEW.temperature < -50 OR NEW.temperature > 100
OR NEW.humidity < 0 OR NEW.humidity > 100
BEGIN
SELECT RAISE(ABORT, 'Invalid sensor values');
END;
-- 场景5:自动通知触发器(配合应用层)
CREATE TRIGGER check_temperature_alert
AFTER INSERT ON sensor_data
FOR EACH ROW
WHEN NEW.temperature > 40.0
BEGIN
INSERT INTO alerts (sensor_id, alert_type, message, value, created_at)
VALUES (NEW.sensor_id, 'HIGH_TEMPERATURE',
'Temperature exceeded 40°C',
NEW.temperature,
CURRENT_TIMESTAMP);
END;
15.3 管理触发器
sql
-- 查看所有触发器
SELECT name FROM sqlite_master WHERE type = 'trigger';
-- 查看触发器定义
SELECT sql FROM sqlite_master WHERE type = 'trigger' AND name = 'trigger_name';
-- 删除触发器
DROP TRIGGER IF EXISTS trigger_name;
-- 临时禁用触发器(通过删除后重建)
-- SQLite 没有直接禁用触发器的命令
16. 存储过程替代方案
SQLite 不支持存储过程,但有以下替代方案:
16.1 使用应用程序代码
python
# Python 实现类似存储过程的功能
class SensorDataProcessor:
def __init__(self, db_path):
self.conn = sqlite3.connect(db_path)
def process_sensor_reading(self, sensor_id, temperature, humidity):
"""类似存储过程:处理传感器读数"""
cursor = self.conn.cursor()
try:
# 开始事务
cursor.execute("BEGIN IMMEDIATE")
# 1. 插入原始数据
cursor.execute('''
INSERT INTO sensor_data (sensor_id, temperature, humidity, timestamp)
VALUES (?, ?, ?, strftime('%s', 'now'))
''', (sensor_id, temperature, humidity))
# 2. 更新最新读数
cursor.execute('''
INSERT OR REPLACE INTO sensor_latest (sensor_id, temperature, humidity, timestamp)
VALUES (?, ?, ?, strftime('%s', 'now'))
''', (sensor_id, temperature, humidity))
# 3. 检查是否需要报警
if temperature > 40.0:
cursor.execute('''
INSERT INTO alerts (sensor_id, alert_type, message, value)
VALUES (?, 'HIGH_TEMP', 'Temperature exceeded 40°C', ?)
''', (sensor_id, temperature))
# 4. 更新统计信息
cursor.execute('''
INSERT OR REPLACE INTO sensor_stats (sensor_id, date, avg_temp, max_temp, min_temp, count)
SELECT
sensor_id,
date(timestamp, 'unixepoch'),
AVG(temperature),
MAX(temperature),
MIN(temperature),
COUNT(*)
FROM sensor_data
WHERE sensor_id = ?
AND date(timestamp, 'unixepoch') = date('now')
GROUP BY sensor_id, date(timestamp, 'unixepoch')
''', (sensor_id,))
# 提交事务
self.conn.commit()
return True
except Exception as e:
self.conn.rollback()
print(f"Error processing sensor reading: {e}")
return False
def get_daily_report(self, date):
"""类似存储过程:生成日报"""
cursor = self.conn.cursor()
cursor.execute('''
SELECT
s.sensor_id,
d.device_name,
d.location,
AVG(s.temperature) as avg_temp,
MAX(s.temperature) as max_temp,
MIN(s.temperature) as min_temp,
AVG(s.humidity) as avg_humidity,
COUNT(*) as reading_count
FROM sensor_data s
JOIN devices d ON s.sensor_id = d.device_id
WHERE date(s.timestamp, 'unixepoch') = ?
GROUP BY s.sensor_id
ORDER BY d.location, s.sensor_id
''', (date,))
return cursor.fetchall()
def cleanup_old_data(self, days_to_keep=30):
"""类似存储过程:清理旧数据"""
cursor = self.conn.cursor()
try:
cursor.execute("BEGIN")
# 删除旧数据
cursor.execute('''
DELETE FROM sensor_data
WHERE timestamp < strftime('%s', 'now', ? || ' days')
''', (f'-{days_to_keep}',))
# 优化数据库
cursor.execute("PRAGMA auto_vacuum = FULL")
cursor.execute("VACUUM")
self.conn.commit()
return cursor.rowcount
except Exception as e:
self.conn.rollback()
raise
16.2 使用 CTE 封装复杂逻辑
sql
-- 使用 CTE 封装复杂查询逻辑(类似视图 + 存储过程)
-- 封装:获取设备健康状态报告
WITH
recent_data AS (
SELECT sensor_id, temperature, humidity, timestamp
FROM sensor_data
WHERE timestamp > strftime('%s', 'now', '-1 hour')
),
device_stats AS (
SELECT
sensor_id,
AVG(temperature) as avg_temp,
MAX(temperature) as max_temp,
MIN(temperature) as min_temp,
COUNT(*) as reading_count
FROM recent_data
GROUP BY sensor_id
),
device_status AS (
SELECT
sensor_id,
CASE
WHEN max_temp > 40 THEN 'CRITICAL'
WHEN max_temp > 35 THEN 'WARNING'
WHEN reading_count < 10 THEN 'LOW_DATA'
ELSE 'NORMAL'
END as status
FROM device_stats
)
SELECT
d.device_name,
d.location,
ds.avg_temp,
ds.max_temp,
ds.min_temp,
ds.reading_count,
dst.status
FROM device_stats ds
JOIN devices d ON ds.sensor_id = d.device_id
JOIN device_status dst ON ds.sensor_id = dst.sensor_id
ORDER BY
CASE dst.status
WHEN 'CRITICAL' THEN 1
WHEN 'WARNING' THEN 2
WHEN 'LOW_DATA' THEN 3
ELSE 4
END;
17. JSON 支持
17.1 JSON 函数(SQLite 3.38.0+)
sql
-- 创建包含 JSON 数据的表
CREATE TABLE devices (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
config JSON,
metadata JSON
);
-- 插入 JSON 数据
INSERT INTO devices (name, config, metadata) VALUES
('温度传感器', '{"interval": 60, "threshold": 40}', '{"location": "机房A", "rack": "R1"}'),
('湿度传感器', '{"interval": 120, "threshold": 80}', '{"location": "机房B", "rack": "R2"}');
-- JSON 函数
-- json():验证 JSON 格式
SELECT json('{"key": "value"}'); -- 返回有效的 JSON
-- json_array():创建 JSON 数组
SELECT json_array(1, 2, 3, 'hello'); -- [1,2,3,"hello"]
-- json_object():创建 JSON 对象
SELECT json_object('name', '张三', 'age', 25); -- {"name":"张三","age":25}
-- json_extract():提取 JSON 值
SELECT json_extract(config, '$.interval') FROM devices;
-- 或使用 -> 操作符
SELECT config -> '$.interval' FROM devices;
-- json_set():设置 JSON 值
UPDATE devices SET config = json_set(config, '$.threshold', 50) WHERE id = 1;
-- json_insert():插入新值(不覆盖)
UPDATE devices SET config = json_insert(config, '$.unit', '°C') WHERE id = 1;
-- json_replace():替换现有值
UPDATE devices SET config = json_replace(config, '$.interval', 30) WHERE id = 1;
-- json_remove():删除值
UPDATE devices SET config = json_remove(config, '$.unit') WHERE id = 1;
-- json_type():获取 JSON 值类型
SELECT json_type(config, '$.interval') FROM devices; -- integer
-- json_valid():验证 JSON 格式
SELECT json_valid('{"key": "value"}'); -- 1
SELECT json_valid('invalid json'); -- 0
-- json_array_length():获取数组长度
SELECT json_array_length('[1,2,3]'); -- 3
-- json_group_array():聚合为 JSON 数组
SELECT json_group_array(name) FROM devices; -- ["温度传感器","湿度传感器"]
-- json_group_object():聚合为 JSON 对象
SELECT json_group_object(name, config) FROM devices;
17.2 JSON 路径查询
sql
-- JSON 路径语法
-- $.key - 对象成员
-- $.key1.key2 - 嵌套对象
-- $[0] - 数组元素
-- $[0].key - 数组中的对象
-- $**.key - 递归搜索
-- 示例数据
CREATE TABLE sensor_readings (
id INTEGER PRIMARY KEY,
data JSON
);
INSERT INTO sensor_readings (data) VALUES
('{"sensors": [{"id": "T1", "value": 25.5}, {"id": "H1", "value": 60}]}'),
('{"sensors": [{"id": "T2", "value": 26.1}, {"id": "H2", "value": 58}]}');
-- 提取嵌套值
SELECT json_extract(data, '$.sensors[0].id') FROM sensor_readings;
-- 结果:T1, T2
-- 提取数组中的所有 id
SELECT json_extract(data, '$.sensors[*].id') FROM sensor_readings;
-- 结果:["T1","H1"], ["T2","H2"]
-- 使用 json_each() 展开 JSON 数组
SELECT json_each.value
FROM sensor_readings, json_each(sensor_readings.data -> '$.sensors');
-- 使用 json_tree() 递归展开
SELECT key, value, type
FROM sensor_readings, json_tree(sensor_readings.data);
17.3 嵌入式 JSON 场景
sql
-- 场景:存储设备配置(灵活的键值对)
CREATE TABLE device_config (
device_id TEXT PRIMARY KEY,
settings JSON NOT NULL
);
-- 插入配置
INSERT INTO device_config (device_id, settings) VALUES
('DEV_001', '{
"network": {
"ip": "192.168.1.100",
"gateway": "192.168.1.1",
"dns": "8.8.8.8"
},
"sensors": ["temperature", "humidity"],
"thresholds": {
"temp_max": 40,
"humidity_max": 80
},
"logging": {
"level": "INFO",
"interval": 60
}
}');
-- 查询特定配置
SELECT json_extract(settings, '$.network.ip') FROM device_config WHERE device_id = 'DEV_001';
-- 更新特定配置
UPDATE device_config
SET settings = json_set(settings, '$.thresholds.temp_max', 45)
WHERE device_id = 'DEV_001';
-- 添加新的传感器类型
UPDATE device_config
SET settings = json_set(settings, '$.sensors',
json_array_append(
json_extract(settings, '$.sensors'),
'pressure'
))
WHERE device_id = 'DEV_001';
-- 查询所有温度阈值超过 40 的设备
SELECT device_id
FROM device_config
WHERE json_extract(settings, '$.thresholds.temp_max') > 40;
-- 使用 JSON 表函数展开数组
SELECT
device_id,
json_each.value as sensor_type
FROM device_config, json_each(device_config.settings -> '$.sensors')
WHERE device_id = 'DEV_001';
18. 全文搜索(FTS5)
18.1 FTS5 基础
sql
-- 创建 FTS5 虚拟表
CREATE VIRTUAL TABLE articles USING fts5(
title,
content,
author
);
-- 插入数据
INSERT INTO articles (title, content, author) VALUES
('SQLite 入门指南', 'SQLite 是一个轻量级的嵌入式数据库...', '张三'),
('嵌入式系统开发', '嵌入式系统是专用的计算机系统...', '李四'),
('物联网技术', '物联网是互联网的延伸...', '王五');
-- 基本搜索
SELECT * FROM articles WHERE articles MATCH 'SQLite';
-- 搜索多个词(AND)
SELECT * FROM articles WHERE articles MATCH 'SQLite 嵌入式';
-- 搜索短语
SELECT * FROM articles WHERE articles MATCH '"轻量级 数据库"';
-- OR 搜索
SELECT * FROM articles WHERE articles MATCH 'SQLite OR 物联网';
-- NOT 搜索
SELECT * FROM articles WHERE articles MATCH 'SQLite NOT 入门';
-- 列指定搜索
SELECT * FROM articles WHERE articles MATCH 'title:SQLite';
SELECT * FROM articles WHERE articles MATCH 'content:数据库';
-- 前缀搜索
SELECT * FROM articles WHERE articles MATCH 'S*';
-- 搜索结果排序(按相关性)
SELECT *, rank FROM articles WHERE articles MATCH 'SQLite' ORDER BY rank;
-- 高亮显示
SELECT highlight(articles, 0, '<b>', '</b>') as highlighted_title,
snippet(articles, 1, '<b>', '</b>', '...', 10) as highlighted_content
FROM articles WHERE articles MATCH 'SQLite';
18.2 FTS5 高级功能
sql
-- 带内容表的 FTS5(节省空间)
CREATE VIRTUAL TABLE articles_fts USING fts5(
title,
content,
content=articles, -- 内容表
content_rowid=id -- 行 ID 映射
);
-- 创建触发器同步数据
CREATE TRIGGER articles_ai AFTER INSERT ON articles BEGIN
INSERT INTO articles_fts(rowid, title, content) VALUES (new.id, new.title, new.content);
END;
CREATE TRIGGER articles_ad AFTER DELETE ON articles BEGIN
INSERT INTO articles_fts(articles_fts, rowid, title, content) VALUES('delete', old.id, old.title, old.content);
END;
CREATE TRIGGER articles_au AFTER UPDATE ON articles BEGIN
INSERT INTO articles_fts(articles_fts, rowid, title, content) VALUES('delete', old.id, old.title, old.content);
INSERT INTO articles_fts(rowid, title, content) VALUES (new.id, new.title, new.content);
END;
-- 分词器配置(中文支持需要自定义分词器)
-- 默认分词器:unicode61
CREATE VIRTUAL TABLE chinese_articles USING fts5(
title,
content,
tokenize='unicode61'
);
-- BM25 排序
SELECT *, bm25(articles_fts) as score
FROM articles_fts
WHERE articles_fts MATCH 'SQLite'
ORDER BY score;
-- 自定义权重
SELECT *, bm25(articles_fts, 10.0, 1.0) as score -- title 权重 10,content 权重 1
FROM articles_fts
WHERE articles_fts MATCH 'SQLite'
ORDER BY score;
18.3 嵌入式场景:设备日志全文搜索
sql
-- 创建日志全文搜索表
CREATE TABLE system_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
level TEXT,
module TEXT,
message TEXT,
details TEXT
);
CREATE VIRTUAL TABLE logs_fts USING fts5(
message,
details,
content=system_logs,
content_rowid=id
);
-- 同步触发器
CREATE TRIGGER logs_ai AFTER INSERT ON system_logs BEGIN
INSERT INTO logs_fts(rowid, message, details) VALUES (new.id, new.message, new.details);
END;
CREATE TRIGGER logs_ad AFTER DELETE ON system_logs BEGIN
INSERT INTO logs_fts(logs_fts, rowid, message, details) VALUES('delete', old.id, old.message, old.details);
END;
-- 搜索错误日志
SELECT s.*, highlight(logs_fts, 0, '**', '**') as highlighted_message
FROM system_logs s
JOIN logs_fts ON s.id = logs_fts.rowid
WHERE logs_fts MATCH 'error OR exception OR fail'
ORDER BY s.timestamp DESC
LIMIT 100;
-- 搜索特定模块的日志
SELECT * FROM system_logs
WHERE module = 'sensor' AND id IN (
SELECT rowid FROM logs_fts WHERE logs_fts MATCH 'timeout'
);
19. 备份与恢复
19.1 SQL 导出/导入
bash
# 导出整个数据库为 SQL 文件
sqlite3 mydb.db .dump > backup.sql
# 导出特定表
sqlite3 mydb.db ".dump users" > users_backup.sql
# 导出为 CSV
sqlite3 mydb.db
.headers on
.mode csv
.output users.csv
SELECT * FROM users;
.output stdout
# 导入 SQL 文件
sqlite3 mydb.db < backup.sql
# 导入 CSV
sqlite3 mydb.db
.mode csv
.import users.csv users
19.2 在线备份 API(C语言)
c
#include <stdio.h>
#include <sqlite3.h>
// 在线备份(不中断服务)
int backup_database(const char *source_path, const char *dest_path) {
sqlite3 *source_db, *dest_db;
sqlite3_backup *backup;
int rc;
// 打开源数据库
rc = sqlite3_open(source_path, &source_db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open source database: %s\n", sqlite3_errmsg(source_db));
return -1;
}
// 打开目标数据库
rc = sqlite3_open(dest_path, &dest_db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open destination database: %s\n", sqlite3_errmsg(dest_db));
sqlite3_close(source_db);
return -1;
}
// 初始化备份
backup = sqlite3_backup_init(dest_db, "main", source_db, "main");
if (backup == NULL) {
fprintf(stderr, "sqlite3_backup_init failed: %s\n", sqlite3_errmsg(dest_db));
sqlite3_close(source_db);
sqlite3_close(dest_db);
return -1;
}
// 执行备份(-1 表示复制所有页面)
do {
rc = sqlite3_backup_step(backup, 100); // 每次复制 100 页
printf("Backup progress: %d/%d pages\n",
sqlite3_backup_remaining(backup),
sqlite3_backup_pagecount(backup));
// 可以在这里添加延迟,减少对主数据库的影响
// usleep(1000);
} while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);
// 完成备份
sqlite3_backup_finish(backup);
if (rc == SQLITE_DONE) {
printf("Backup completed successfully\n");
} else {
fprintf(stderr, "Backup failed: %s\n", sqlite3_errmsg(dest_db));
}
sqlite3_close(source_db);
sqlite3_close(dest_db);
return (rc == SQLITE_DONE) ? 0 : -1;
}
// 定期备份函数(嵌入式场景)
void *backup_thread(void *arg) {
while (1) {
// 每小时备份一次
sleep(3600);
// 生成带时间戳的备份文件名
char backup_path[256];
time_t now = time(NULL);
strftime(backup_path, sizeof(backup_path),
"backup_%Y%m%d_%H%M%S.db", localtime(&now));
backup_database("sensor_data.db", backup_path);
// 清理超过7天的备份
cleanup_old_backups(7);
}
return NULL;
}
19.3 文件系统级别备份
bash
# 方法1:直接复制文件(需要确保没有写入操作)
cp mydb.db mydb_backup.db
# 方法2:使用 SQLite 的 .backup 命令(推荐)
sqlite3 mydb.db ".backup mydb_backup.db"
# 方法3:使用 VACUUM INTO(SQLite 3.27.0+)
sqlite3 mydb.db "VACUUM INTO 'mydb_backup.db';"
# 方法4:使用 SQLite 的在线备份命令
sqlite3 mydb.db
.backup main mydb_backup.db
.quit
# 嵌入式场景:使用 rsync 进行增量备份
rsync -av --progress mydb.db /backup/location/
# 嵌入式场景:使用 cron 定时备份
# 0 2 * * * /usr/bin/sqlite3 /data/mydb.db ".backup /backup/mydb_$(date +\%Y\%m\%d).db"
19.4 数据库恢复
sql
-- 从 SQL 备份恢复
sqlite3 new_database.db < backup.sql
-- 从损坏的数据库恢复数据
-- 步骤1:尝试导出数据
sqlite3 corrupted.db ".dump" > recovered.sql
-- 步骤2:创建新数据库并导入
sqlite3 new_database.db < recovered.sql
-- 步骤3:如果部分数据损坏,使用 .recover 命令(SQLite 3.33.0+)
sqlite3 corrupted.db ".recover" > recovered.sql
sqlite3 new_database.db < recovered.sql
-- 步骤4:检查数据库完整性
PRAGMA integrity_check;
-- 输出 "ok" 表示完整
-- 步骤5:修复数据库
PRAGMA writable_schema = ON;
-- 执行修复操作
PRAGMA writable_schema = OFF;
20. 性能优化(嵌入式重点)
20.1 查询优化
sql
-- 1. 使用 EXPLAIN QUERY PLAN 分析查询
EXPLAIN QUERY PLAN
SELECT * FROM sensor_data WHERE sensor_id = 'TEMP_001' AND timestamp > 1000;
-- 输出示例:
-- SEARCH TABLE sensor_data USING INDEX idx_sensor_data (sensor_id=? AND timestamp>?)
-- 2. 避免 SELECT *,只选择需要的列
-- 慢
SELECT * FROM sensor_data;
-- 快
SELECT sensor_id, temperature FROM sensor_data;
-- 3. 使用 LIMIT 限制结果集
SELECT sensor_id, temperature FROM sensor_data
WHERE timestamp > strftime('%s', 'now', '-1 hour')
LIMIT 100;
-- 4. 避免在 WHERE 子句中使用函数(会导致索引失效)
-- 慢(索引失效)
SELECT * FROM sensor_data WHERE date(timestamp, 'unixepoch') = '2024-01-01';
-- 快(使用索引)
SELECT * FROM sensor_data
WHERE timestamp >= strftime('%s', '2024-01-01')
AND timestamp < strftime('%s', '2024-01-02');
-- 5. 使用覆盖索引
CREATE INDEX idx_sensor_covering ON sensor_data(sensor_id, timestamp, temperature, humidity);
-- 以下查询可以完全从索引中获取数据
SELECT sensor_id, timestamp, temperature FROM sensor_data WHERE sensor_id = 'TEMP_001';
-- 6. 避免使用 OR(改用 UNION)
-- 慢
SELECT * FROM users WHERE city = 'Beijing' OR city = 'Shanghai';
-- 快
SELECT * FROM users WHERE city = 'Beijing'
UNION ALL
SELECT * FROM users WHERE city = 'Shanghai';
-- 7. 使用 EXISTS 代替 IN(大数据集)
-- 慢
SELECT * FROM users WHERE id IN (SELECT user_id FROM orders);
-- 快
SELECT * FROM users u WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.id);
20.2 索引优化策略
sql
-- 嵌入式传感器数据索引策略
-- 1. 时间范围查询索引(最常用)
CREATE INDEX idx_sensor_data_time ON sensor_data(timestamp);
-- 2. 设备+时间复合索引
CREATE INDEX idx_sensor_data_device_time ON sensor_data(sensor_id, timestamp);
-- 3. 覆盖索引(包含常用查询字段)
CREATE INDEX idx_sensor_data_covering ON sensor_data(
sensor_id, timestamp, temperature, humidity
);
-- 4. 部分索引(只索引最近的数据)
CREATE INDEX idx_sensor_data_recent ON sensor_data(sensor_id, timestamp)
WHERE timestamp > strftime('%s', 'now', '-7 days');
-- 5. 分析索引使用情况
ANALYZE;
-- 查看统计信息
SELECT * FROM sqlite_stat1 WHERE tbl = 'sensor_data';
20.3 内存优化
sql
-- 嵌入式系统内存优化 PRAGMA 配置
-- 1. 减少缓存大小(内存紧张时)
PRAGMA cache_size = -1000; -- 1MB 缓存
-- 2. 使用内存临时存储
PRAGMA temp_store = MEMORY;
-- 3. 禁用 mmap(如果内存紧张)
PRAGMA mmap_size = 0;
-- 4. 使用较小的页面大小
PRAGMA page_size = 1024; -- 1KB 页面
-- 5. 启用自动清理
PRAGMA auto_vacuum = INCREMENTAL;
-- 6. 定期执行增量清理
PRAGMA incremental_vacuum(100);
-- 7. 压缩数据库
VACUUM;
-- 8. 检查数据库大小
PRAGMA page_count;
PRAGMA page_size;
-- 计算:page_count * page_size = 数据库大小
20.4 写入优化
sql
-- 嵌入式写入优化策略
-- 1. 使用事务批量写入
BEGIN;
INSERT INTO sensor_data VALUES (...);
INSERT INTO sensor_data VALUES (...);
-- ... 100-1000 条记录 ...
COMMIT;
-- 2. 使用 WAL 模式
PRAGMA journal_mode = WAL;
-- 3. 调整同步模式(性能 vs 安全)
PRAGMA synchronous = NORMAL; -- 比 FULL 快,安全性略低
-- 4. 使用预编译语句(减少解析开销)
-- 在 C API 中使用 sqlite3_prepare_v2
-- 5. 延迟索引更新
-- 大批量导入时先删除索引,导入后重建
DROP INDEX idx_sensor_data;
-- ... 批量导入 ...
CREATE INDEX idx_sensor_data ON sensor_data(sensor_id, timestamp);
-- 6. 使用 INSERT OR IGNORE 跳过重复数据
INSERT OR IGNORE INTO sensor_data VALUES (...);
-- 7. 调整页面缓存大小
PRAGMA cache_size = -8000; -- 8MB
-- 8. 使用 EXCLUSIVE 事务(批量写入时)
BEGIN EXCLUSIVE;
-- ... 批量写入 ...
COMMIT;
20.5 数据库维护
sql
-- 定期维护操作
-- 1. 更新统计信息(帮助优化器选择最佳查询计划)
ANALYZE;
-- 2. 压缩数据库(回收删除数据的空间)
VACUUM;
-- 3. 增量清理(减少碎片)
PRAGMA auto_vacuum = INCREMENTAL;
PRAGMA incremental_vacuum;
-- 4. 检查数据库完整性
PRAGMA integrity_check;
PRAGMA quick_check;
-- 5. 重建索引
REINDEX;
REINDEX idx_sensor_data;
-- 6. 清理 WAL 文件
PRAGMA wal_checkpoint(TRUNCATE);
-- 7. 查看数据库信息
PRAGMA page_count;
PRAGMA page_size;
PRAGMA freelist_count;
PRAGMA cache_size;
-- 嵌入式场景:定期维护脚本
-- 每天凌晨 3 点执行
-- 0 3 * * * sqlite3 /data/sensor.db "ANALYZE; VACUUM; PRAGMA wal_checkpoint(TRUNCATE);"
21. 安全注意事项
21.1 SQL 注入防护
c
// 错误方式:字符串拼接(存在 SQL 注入风险)
char sql[256];
sprintf(sql, "SELECT * FROM users WHERE username = '%s'", user_input);
sqlite3_exec(db, sql, callback, 0, 0); // 危险!
// 正确方式:使用参数化查询
const char *sql = "SELECT * FROM users WHERE username = ?";
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, user_input, -1, SQLITE_STATIC);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
// Python 示例
# 错误方式
cursor.execute(f"SELECT * FROM users WHERE username = '{user_input}'")
# 正确方式
cursor.execute("SELECT * FROM users WHERE username = ?", (user_input,))
21.2 数据加密
sql
-- SQLite 本身不支持加密,但可以使用以下方案:
-- 1. SQLCipher(开源加密扩展)
-- 编译时集成 SQLCipher
-- 使用方式与普通 SQLite 相同,但数据文件加密
-- 2. 应用层加密
-- 在插入前加密敏感字段,查询后解密
-- 3. 文件系统加密
-- 使用 LUKS(Linux)或 BitLocker(Windows)加密整个分区
21.3 访问控制
c
// 设置数据库文件权限
#include <sys/stat.h>
// 创建数据库时设置权限
sqlite3_open("sensor_data.db", &db);
chmod("sensor_data.db", 0600); // 只有所有者可读写
// 使用 sqlite3_open_v2 限制访问模式
sqlite3_open_v2(
"sensor_data.db",
&db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX,
NULL
);
// 只读模式打开
sqlite3_open_v2(
"sensor_data.db",
&db,
SQLITE_OPEN_READONLY,
NULL
);
21.4 敏感数据处理
sql
-- 1. 使用 secure_delete 覆盖已删除数据
PRAGMA secure_delete = ON;
-- 2. 定期清理敏感日志
DELETE FROM system_logs WHERE log_level = 'SENSITIVE';
-- 3. 避免在数据库中存储明文密码
-- 错误
INSERT INTO users (username, password) VALUES ('admin', '123456');
-- 正确
INSERT INTO users (username, password_hash) VALUES ('admin', 'hashed_password');
-- 4. 使用参数化查询(防止 SQL 注入)
-- 见 21.1 节
22. 嵌入式行业实战案例
22.1 IoT 网关数据采集系统
sql
-- 数据库设计
CREATE TABLE devices (
device_id TEXT PRIMARY KEY,
device_name TEXT NOT NULL,
device_type TEXT NOT NULL,
location TEXT,
config JSON,
last_seen TIMESTAMP,
is_online INTEGER DEFAULT 0
);
CREATE TABLE sensor_data (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT NOT NULL,
sensor_type TEXT NOT NULL,
value REAL NOT NULL,
unit TEXT,
timestamp INTEGER NOT NULL, -- Unix 时间戳
quality INTEGER DEFAULT 100, -- 数据质量 0-100
FOREIGN KEY (device_id) REFERENCES devices(device_id)
);
CREATE TABLE alerts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
device_id TEXT NOT NULL,
alert_type TEXT NOT NULL,
message TEXT NOT NULL,
value REAL,
threshold REAL,
is_resolved INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
resolved_at TIMESTAMP,
FOREIGN KEY (device_id) REFERENCES devices(device_id)
);
-- 索引优化
CREATE INDEX idx_sensor_data_device_time ON sensor_data(device_id, timestamp);
CREATE INDEX idx_sensor_data_time ON sensor_data(timestamp);
CREATE INDEX idx_alerts_device ON alerts(device_id, created_at);
-- 查询最近1小时的设备数据
SELECT
d.device_name,
d.location,
s.sensor_type,
AVG(s.value) as avg_value,
MAX(s.value) as max_value,
MIN(s.value) as min_value,
COUNT(*) as reading_count
FROM sensor_data s
JOIN devices d ON s.device_id = d.device_id
WHERE s.timestamp > strftime('%s', 'now', '-1 hour')
GROUP BY d.device_id, s.sensor_type;
-- 查询设备告警统计
SELECT
d.device_name,
a.alert_type,
COUNT(*) as alert_count,
MAX(a.created_at) as last_alert
FROM alerts a
JOIN devices d ON a.device_id = d.device_id
WHERE a.created_at > datetime('now', '-7 days')
GROUP BY d.device_id, a.alert_type
ORDER BY alert_count DESC;
22.2 工业控制系统参数管理
sql
-- 系统参数表
CREATE TABLE system_parameters (
param_id TEXT PRIMARY KEY,
param_name TEXT NOT NULL,
param_value TEXT NOT NULL,
value_type TEXT NOT NULL, -- int, float, string, bool, json
min_value REAL,
max_value REAL,
default_value TEXT,
description TEXT,
category TEXT,
is_critical INTEGER DEFAULT 0,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_by TEXT
);
-- 参数修改历史
CREATE TABLE parameter_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
param_id TEXT NOT NULL,
old_value TEXT,
new_value TEXT NOT NULL,
changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
changed_by TEXT NOT NULL,
reason TEXT,
FOREIGN KEY (param_id) REFERENCES system_parameters(param_id)
);
-- 插入系统参数
INSERT INTO system_parameters VALUES
('motor_speed', '电机转速', '1500', 'int', 0, 3000, '1500', '电机转速(RPM)', '电机', 1, CURRENT_TIMESTAMP, 'admin'),
('temp_threshold', '温度阈值', '85.0', 'float', -40, 150, '85.0', '温度报警阈值(°C)', '传感器', 1, CURRENT_TIMESTAMP, 'admin'),
('pressure_limit', '压力上限', '10.0', 'float', 0, 20, '10.0', '压力上限(MPa)', '传感器', 1, CURRENT_TIMESTAMP, 'admin');
-- 更新参数(带历史记录)
BEGIN;
INSERT INTO parameter_history (param_id, old_value, new_value, changed_by, reason)
SELECT param_id, param_value, '1800', 'engineer', '提高生产效率'
FROM system_parameters WHERE param_id = 'motor_speed';
UPDATE system_parameters SET param_value = '1800', updated_at = CURRENT_TIMESTAMP, updated_by = 'engineer'
WHERE param_id = 'motor_speed';
COMMIT;
-- 查询参数修改历史
SELECT
p.param_name,
ph.old_value,
ph.new_value,
ph.changed_at,
ph.changed_by,
ph.reason
FROM parameter_history ph
JOIN system_parameters p ON ph.param_id = p.param_id
WHERE ph.param_id = 'motor_speed'
ORDER BY ph.changed_at DESC;
22.3 车载信息娱乐系统
sql
-- 媒体库管理
CREATE TABLE media_library (
id INTEGER PRIMARY KEY AUTOINCREMENT,
file_path TEXT NOT NULL UNIQUE,
file_name TEXT NOT NULL,
file_type TEXT NOT NULL, -- audio, video, image
file_size INTEGER,
duration INTEGER, -- 秒
title TEXT,
artist TEXT,
album TEXT,
genre TEXT,
year INTEGER,
thumbnail_path TEXT,
last_played TIMESTAMP,
play_count INTEGER DEFAULT 0,
rating INTEGER DEFAULT 0, -- 0-5
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 播放列表
CREATE TABLE playlists (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE playlist_items (
playlist_id INTEGER NOT NULL,
media_id INTEGER NOT NULL,
position INTEGER NOT NULL,
added_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (playlist_id, media_id),
FOREIGN KEY (playlist_id) REFERENCES playlists(id) ON DELETE CASCADE,
FOREIGN KEY (media_id) REFERENCES media_library(id) ON DELETE CASCADE
);
-- 播放历史
CREATE TABLE play_history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
media_id INTEGER NOT NULL,
played_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
play_duration INTEGER, -- 实际播放时长
completed INTEGER DEFAULT 0, -- 是否播放完成
FOREIGN KEY (media_id) REFERENCES media_library(id)
);
-- 创建索引
CREATE INDEX idx_media_library_type ON media_library(file_type);
CREATE INDEX idx_media_library_artist ON media_library(artist);
CREATE INDEX idx_play_history_media ON play_history(media_id, played_at);
-- 最常播放的歌曲
SELECT
title,
artist,
play_count,
last_played
FROM media_library
WHERE file_type = 'audio'
ORDER BY play_count DESC
LIMIT 10;
-- 最近播放的歌曲
SELECT
m.title,
m.artist,
ph.played_at,
ph.play_duration
FROM play_history ph
JOIN media_library m ON ph.media_id = m.id
ORDER BY ph.played_at DESC
LIMIT 20;
22.4 智能家居控制系统
sql
-- 设备管理
CREATE TABLE home_devices (
device_id TEXT PRIMARY KEY,
device_name TEXT NOT NULL,
device_type TEXT NOT NULL, -- light, thermostat, lock, camera, sensor
room TEXT,
manufacturer TEXT,
model TEXT,
firmware_version TEXT,
ip_address TEXT,
mac_address TEXT,
is_online INTEGER DEFAULT 0,
last_seen TIMESTAMP,
config JSON -- 设备特定配置
);
-- 设备状态
CREATE TABLE device_states (
device_id TEXT NOT NULL,
state_key TEXT NOT NULL,
state_value TEXT NOT NULL,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (device_id, state_key),
FOREIGN KEY (device_id) REFERENCES home_devices(device_id)
);
-- 自动化规则
CREATE TABLE automation_rules (
rule_id INTEGER PRIMARY KEY AUTOINCREMENT,
rule_name TEXT NOT NULL,
trigger_type TEXT NOT NULL, -- time, sensor, device_state
trigger_config JSON NOT NULL,
action_type TEXT NOT NULL, -- device_command, notification, scene
action_config JSON NOT NULL,
is_enabled INTEGER DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 场景
CREATE TABLE scenes (
scene_id INTEGER PRIMARY KEY AUTOINCREMENT,
scene_name TEXT NOT NULL,
description TEXT,
actions JSON NOT NULL, -- 设备动作列表
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 定时任务执行日志
CREATE TABLE automation_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
rule_id INTEGER,
scene_id INTEGER,
triggered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
trigger_reason TEXT,
actions_executed JSON,
status TEXT, -- success, failed, partial
error_message TEXT,
FOREIGN KEY (rule_id) REFERENCES automation_rules(rule_id),
FOREIGN KEY (scene_id) REFERENCES scenes(scene_id)
);
-- 查询所有在线设备
SELECT device_name, device_type, room, ip_address, last_seen
FROM home_devices
WHERE is_online = 1
ORDER BY room, device_type;
-- 查询设备状态
SELECT
h.device_name,
h.room,
ds.state_key,
ds.state_value,
ds.updated_at
FROM device_states ds
JOIN home_devices h ON ds.device_id = h.device_id
WHERE h.room = '客厅'
ORDER BY h.device_name, ds.state_key;
-- 查询自动化规则执行情况
SELECT
r.rule_name,
COUNT(*) as execution_count,
SUM(CASE WHEN al.status = 'success' THEN 1 ELSE 0 END) as success_count,
SUM(CASE WHEN al.status = 'failed' THEN 1 ELSE 0 END) as failed_count,
MAX(al.triggered_at) as last_execution
FROM automation_log al
JOIN automation_rules r ON al.rule_id = r.rule_id
WHERE al.triggered_at > datetime('now', '-7 days')
GROUP BY r.rule_id
ORDER BY execution_count DESC;
22.5 医疗设备数据记录
sql
-- 患者信息(脱敏示例)
CREATE TABLE patients (
patient_id TEXT PRIMARY KEY,
gender TEXT,
birth_year INTEGER,
blood_type TEXT
);
-- 生命体征记录
CREATE TABLE vital_signs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
patient_id TEXT NOT NULL,
device_id TEXT NOT NULL,
heart_rate INTEGER, -- 心率
blood_pressure_sys INTEGER, -- 收缩压
blood_pressure_dia INTEGER, -- 舒张压
oxygen_saturation REAL, -- 血氧饱和度
temperature REAL, -- 体温
respiratory_rate INTEGER, -- 呼吸率
timestamp INTEGER NOT NULL, -- Unix 时间戳
is_alert INTEGER DEFAULT 0,
FOREIGN KEY (patient_id) REFERENCES patients(patient_id)
);
-- 告警记录
CREATE TABLE vital_alerts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
patient_id TEXT NOT NULL,
device_id TEXT NOT NULL,
alert_type TEXT NOT NULL,
vital_type TEXT NOT NULL,
value REAL NOT NULL,
threshold_low REAL,
threshold_high REAL,
severity TEXT NOT NULL, -- low, medium, high, critical
acknowledged INTEGER DEFAULT 0,
acknowledged_by TEXT,
acknowledged_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (patient_id) REFERENCES patients(patient_id)
);
-- 索引
CREATE INDEX idx_vital_signs_patient_time ON vital_signs(patient_id, timestamp);
CREATE INDEX idx_vital_signs_device ON vital_signs(device_id, timestamp);
CREATE INDEX idx_vital_alerts_patient ON vital_alerts(patient_id, created_at);
-- 查询患者最近的生命体征
SELECT
heart_rate,
blood_pressure_sys,
blood_pressure_dia,
oxygen_saturation,
temperature,
datetime(timestamp, 'unixepoch', 'localtime') as recorded_at
FROM vital_signs
WHERE patient_id = 'P001'
ORDER BY timestamp DESC
LIMIT 1;
-- 查询异常生命体征
SELECT
v.patient_id,
v.heart_rate,
v.blood_pressure_sys,
v.blood_pressure_dia,
v.oxygen_saturation,
v.temperature,
datetime(v.timestamp, 'unixepoch', 'localtime') as recorded_at
FROM vital_signs v
WHERE v.is_alert = 1
AND v.timestamp > strftime('%s', 'now', '-24 hours')
ORDER BY v.timestamp DESC;
-- 查询未确认的告警
SELECT
va.alert_type,
va.vital_type,
va.value,
va.severity,
va.created_at,
p.patient_id
FROM vital_alerts va
JOIN patients p ON va.patient_id = p.patient_id
WHERE va.acknowledged = 0
ORDER BY
CASE va.severity
WHEN 'critical' THEN 1
WHEN 'high' THEN 2
WHEN 'medium' THEN 3
ELSE 4
END,
va.created_at DESC;
23. 常见问题与解决方案
23.1 数据库锁定问题
sql
-- 问题:数据库被锁定,无法写入
-- 原因:多个连接同时写入,或事务未正确关闭
-- 解决方案1:使用 WAL 模式
PRAGMA journal_mode = WAL;
-- 解决方案2:设置忙等待超时
-- C API
sqlite3_busy_timeout(db, 5000); -- 5秒超时
-- 解决方案3:使用 IMMEDIATE 事务
BEGIN IMMEDIATE;
-- ... 操作 ...
COMMIT;
-- 解决方案4:检查是否有未关闭的事务
-- 查看当前锁状态
PRAGMA lock_count;
23.2 数据库损坏修复
sql
-- 问题:数据库损坏,无法打开
-- 解决方案1:尝试恢复数据
sqlite3 corrupted.db ".recover" > recovered.sql
sqlite3 new_database.db < recovered.sql
-- 解决方案2:使用备份恢复
sqlite3 corrupted.db ".backup" > backup.sql
-- 解决方案3:检查完整性
PRAGMA integrity_check;
-- 解决方案4:强制修复(可能丢失数据)
PRAGMA writable_schema = ON;
DELETE FROM sqlite_master WHERE type = 'table' AND name = 'corrupted_table';
PRAGMA writable_schema = OFF;
23.3 性能问题排查
sql
-- 问题:查询速度慢
-- 排查步骤1:查看执行计划
EXPLAIN QUERY PLAN SELECT * FROM slow_query;
-- 排查步骤2:检查是否使用了索引
-- 如果输出包含 "SCAN TABLE",说明没有使用索引
-- 如果输出包含 "SEARCH TABLE ... USING INDEX",说明使用了索引
-- 排查步骤3:更新统计信息
ANALYZE;
-- 排查步骤4:检查索引是否合理
.indices table_name;
-- 排查步骤5:检查数据库碎片
PRAGMA freelist_count;
PRAGMA page_count;
-- 碎片率 = freelist_count / page_count
-- 解决方案:定期维护
ANALYZE;
VACUUM;
PRAGMA wal_checkpoint(TRUNCATE);
23.4 并发访问问题
c
// 问题:多线程/多进程并发访问
// 解决方案1:使用连接池
// 每个线程使用独立的连接
// 解决方案2:使用序列化模式
sqlite3_open_v2(
"database.db",
&db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX,
NULL
);
// 解决方案3:使用互斥锁保护
pthread_mutex_lock(&db_mutex);
// ... 数据库操作 ...
pthread_mutex_unlock(&db_mutex);
// 解决方案4:使用 WAL 模式提高并发性能
sqlite3_exec(db, "PRAGMA journal_mode=WAL;", 0, 0, 0);
23.5 内存不足问题
sql
-- 问题:嵌入式系统内存不足
-- 解决方案1:减少缓存大小
PRAGMA cache_size = -500; -- 500KB
-- 解决方案2:禁用 mmap
PRAGMA mmap_size = 0;
-- 解决方案3:使用较小的页面大小
PRAGMA page_size = 1024;
-- 解决方案4:使用内存临时存储
PRAGMA temp_store = MEMORY;
-- 解决方案5:定期清理数据库
DELETE FROM old_data WHERE timestamp < strftime('%s', 'now', '-30 days');
VACUUM;
-- 解决方案6:使用编译时裁剪
-- 编译时定义以下宏减少内存占用:
-- SQLITE_OMIT_PROGRESS_CALLBACK
-- SQLITE_OMIT_LOAD_EXTENSION
-- SQLITE_DEFAULT_MEMSTATUS=0
23.6 数据迁移问题
sql
-- 问题:数据库 schema 变更
-- 解决方案1:使用 user_version 进行版本控制
PRAGMA user_version = 2;
-- 解决方案2:编写迁移脚本
-- 检查当前版本
-- 如果是 v1,执行迁移到 v2
-- 如果是 v2,执行迁移到 v3
-- ...
-- 解决方案3:使用 ALTER TABLE(SQLite 3.25.0+ 支持重命名列)
ALTER TABLE users RENAME COLUMN old_name TO new_name;
-- 解决方案4:重建表(修改不支持的结构)
-- 见第 4.3 节
附录
A. SQLite3 命令行速查表
bash
# 数据库操作
.open filename.db # 打开数据库
.backup filename.db # 备份数据库
.import file.csv table # 导入 CSV
.dump > file.sql # 导出 SQL
.read file.sql # 执行 SQL 文件
.quit # 退出
# 显示设置
.mode column|csv|json|line|markdown|table|tabs
.headers on|off
.nullvalue string
.output file|stdout
.timer on|off
# 数据库信息
.tables # 列出所有表
.schema table # 显示表结构
.indices table # 显示索引
.dbinfo # 数据库信息
.show # 显示当前设置
# 实用命令
.explain # 设置解释模式
.eqp on|off # 执行计划
.stats on|off # 统计信息
B. PRAGMA 速查表
sql
-- 性能相关
PRAGMA cache_size = -KB; -- 缓存大小
PRAGMA page_size = bytes; -- 页面大小
PRAGMA journal_mode = WAL; -- 日志模式
PRAGMA synchronous = NORMAL; -- 同步模式
PRAGMA temp_store = MEMORY; -- 临时存储
PRAGMA mmap_size = bytes; -- 内存映射
-- 安全相关
PRAGMA foreign_keys = ON; -- 外键约束
PRAGMA secure_delete = ON; -- 安全删除
-- 维护相关
PRAGMA integrity_check; -- 完整性检查
PRAGMA auto_vacuum = INCREMENTAL; -- 自动清理
PRAGMA wal_checkpoint; -- WAL 检查点
-- 信息查询
PRAGMA database_list; -- 数据库列表
PRAGMA table_info(table); -- 表信息
PRAGMA index_list(table); -- 索引列表
PRAGMA compile_options; -- 编译选项
PRAGMA user_version; -- 用户版本
C. C API 速查表
c
// 数据库操作
sqlite3_open(filename, &db);
sqlite3_open_v2(filename, &db, flags, vfs);
sqlite3_close(db);
// 语句操作
sqlite3_prepare_v2(db, sql, -1, &stmt, NULL);
sqlite3_step(stmt);
sqlite3_finalize(stmt);
sqlite3_reset(stmt);
// 参数绑定
sqlite3_bind_int(stmt, index, value);
sqlite3_bind_double(stmt, index, value);
sqlite3_bind_text(stmt, index, value, length, destructor);
sqlite3_bind_blob(stmt, index, value, length, destructor);
sqlite3_bind_null(stmt, index);
// 列访问
sqlite3_column_int(stmt, index);
sqlite3_column_double(stmt, index);
sqlite3_column_text(stmt, index);
sqlite3_column_blob(stmt, index);
sqlite3_column_bytes(stmt, index);
// 错误处理
sqlite3_errmsg(db);
sqlite3_errcode(db);
sqlite3_extended_errcode(db);
// 执行 SQL
sqlite3_exec(db, sql, callback, arg, &errmsg);
D. Python API 速查表
python
import sqlite3
# 连接
conn = sqlite3.connect('database.db')
conn = sqlite3.connect(':memory:')
conn = sqlite3.connect('file:database?mode=ro', uri=True)
# 游标
cursor = conn.cursor()
# 执行 SQL
cursor.execute('SELECT * FROM users')
cursor.executemany('INSERT INTO users VALUES (?, ?)', data)
cursor.executescript('''
CREATE TABLE test (id INTEGER);
INSERT INTO test VALUES (1);
''')
# 获取结果
row = cursor.fetchone() # 一行
rows = cursor.fetchmany(10) # 多行
rows = cursor.fetchall() # 所有行
# 事务
conn.commit()
conn.rollback()
# 关闭
cursor.close()
conn.close()
总结
嵌入式 SQLite3 最佳实践清单
| 类别 | 实践 | 说明 |
|---|---|---|
| 日志模式 | 使用 WAL | 提高并发性能 |
| 同步模式 | NORMAL | 平衡性能和安全 |
| 缓存大小 | 根据内存调整 | 内存紧张时减小 |
| 临时存储 | MEMORY | 减少磁盘 I/O |
| 外键约束 | 启用 | 保证数据完整性 |
| 事务 | 批量操作使用事务 | 提高写入性能 |
| 索引 | 为常用查询创建索引 | 提高查询性能 |
| 预编译语句 | 使用 prepared statements | 减少解析开销 |
| 参数化查询 | 防止 SQL 注入 | 安全性 |
| 定期维护 | ANALYZE + VACUUM | 保持性能 |
| 备份 | 定期备份 | 数据安全 |
| 错误处理 | 检查返回值 | 健壮性 |
| 线程安全 | 使用 FULLMUTEX | 并发安全 |
| 编译裁剪 | 裁剪不需要的功能 | 减少体积 |
文档版本 :1.0
最后更新 :2026年6月30日
适用版本 :SQLite 3.45+
适用场景:嵌入式系统、IoT、移动应用、桌面软件