SQLite3 完整操作指南:从入门到精通

SQLite3 完整操作指南:从入门到精通

适用于嵌入式开发、物联网、移动应用及桌面软件开发


目录

  1. [SQLite3 概述](#SQLite3 概述)
  2. 安装与环境配置
  3. 数据库基础操作
  4. 表操作(DDL)
  5. 数据类型详解
  6. [CRUD 操作](#CRUD 操作)
  7. 高级查询
  8. 索引优化
  9. 视图
  10. 事务处理
  11. [PRAGMA 配置(嵌入式重点)](#PRAGMA 配置(嵌入式重点))
  12. [WAL 模式详解](#WAL 模式详解)
  13. [C/C++ API 编程(嵌入式核心)](#C/C++ API 编程(嵌入式核心))
  14. [Python 操作 SQLite3](#Python 操作 SQLite3)
  15. 触发器
  16. 存储过程替代方案
  17. [JSON 支持](#JSON 支持)
  18. 全文搜索(FTS5)
  19. 备份与恢复
  20. 性能优化(嵌入式重点)
  21. 安全注意事项
  22. 嵌入式行业实战案例
  23. 常见问题与解决方案

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、移动应用、桌面软件