SQLite数据库基础:特点、创建与约束
各位同学,今天我们来聊聊SQLite。说实话,我第一次接触SQLite是在做一个嵌入式项目的时候。当时要在资源受限的设备上存数据,MySQL太重了,文件型数据库又不够用。后来同事推荐了SQLite,一试就爱上了。嗯,今天我就把这些年积累的经验分享给大家。
SQLite的独特之处
SQLite不是那种需要安装服务的数据库。它就是一个库文件,直接嵌入到你的程序里。我经常跟新人说:SQLite就是数据库界的瑞士军刀——小巧、实用、不需要额外配置。
它的核心特点,我总结为以下几点:
- 零配置:不需要安装、不需要管理员、不需要启动服务。你调用API,它就开始工作。
- 单文件存储:整个数据库就是一个文件。我在项目中经常用它做数据交换格式,比XML和JSON方便多了。
- 轻量级:库文件只有几百KB。你想想看,Qt程序里嵌入一个SQLite,几乎不增加体积。
- 事务完整:支持ACID事务。别因为它小就小看它,该有的功能一个不少。
- 跨平台:Windows、Linux、macOS、Android、iOS都能跑。我曾在树莓派上跑过,稳得很。
适用场景:移动应用、嵌入式设备、桌面软件、测试环境、中小型网站(读多写少的情况)。
不适用场景:高并发写入、多用户同时写、数据量超过TB级别。
创建数据库——就这么简单
在Qt里操作SQLite,第一步就是创建数据库。说白了,就是指定一个文件路径。如果文件不存在,SQLite会自动创建。
// 创建/打开SQLite数据库
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
db.setDatabaseName("myapp.db"); // 数据库文件名
if (!db.open()) {
qDebug() << "数据库打开失败:" << db.lastError().text();
return;
}
// 此时数据库文件已经创建好了
qDebug() << "数据库创建成功!";
我个人习惯把数据库文件放在应用程序的数据目录下。曾经有个项目,我把数据库文件直接放在程序目录,结果用户没有写权限,折腾了半天。后来统一用QStandardPaths获取用户数据目录,再也没出过问题。
小技巧:如果你只想在内存中操作数据,可以用:memory:作为数据库名。程序退出后数据自动消失,适合做临时计算。
数据类型——SQLite的灵活之处
SQLite的数据类型和其他数据库不太一样。它只有5种存储类(Storage Class),但支持灵活的类型亲和性(Type Affinity)。
| 存储类 | 说明 | 示例 |
|---|---|---|
| NULL | 空值 | NULL |
| INTEGER | 有符号整数(1-8字节) | 42, -100, 0 |
| REAL | 浮点数(8字节IEEE) | 3.14, -0.001 |
| TEXT | 字符串(UTF-8/16) | 'Hello', "张三" |
| BLOB | 二进制数据 | 图片、文件内容 |
这里有个坑,我必须要提醒大家:SQLite对类型检查很宽松。你可以在INTEGER列里插入字符串,它不会报错。为什么会这样?因为SQLite采用的是动态类型系统。我记得有一次,同事往年龄字段里存了"十八岁"这个字符串,查询时做排序,结果全乱了。所以,写代码时一定要自己做好类型校验。
创建表——实战演练
创建表的语法和其他SQL数据库基本一致。我们以用户信息表为例:
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
password TEXT NOT NULL,
email TEXT,
age INTEGER DEFAULT 0,
created_at TEXT DEFAULT (datetime('now', 'localtime'))
);
这段代码里,我用了IF NOT EXISTS。为什么?因为程序可能多次启动,每次启动都创建表会报错。加上这个判断,表存在就跳过,省心。
在Qt中执行建表语句:
QSqlQuery query;
QString sql = "CREATE TABLE IF NOT EXISTS users ("
"id INTEGER PRIMARY KEY AUTOINCREMENT, "
"username TEXT NOT NULL UNIQUE, "
"password TEXT NOT NULL, "
"email TEXT, "
"age INTEGER DEFAULT 0, "
"created_at TEXT DEFAULT (datetime('now', 'localtime'))"
")";
if (!query.exec(sql)) {
qDebug() << "建表失败:" << query.lastError().text();
} else {
qDebug() << "表创建成功!";
}
注意:SQLite的AUTOINCREMENT和MySQL的AUTO_INCREMENT不同。SQLite中,如果不指定AUTOINCREMENT,主键也会自增,但可能会复用已删除的ID。加上AUTOINCREMENT后,ID只会递增,不会复用。不过,这会带来一点点性能开销。
约束条件——保证数据质量
约束条件就是给数据加规矩。没有规矩,不成方圆。我在项目中见过太多因为缺少约束导致的数据混乱。
常用的约束条件有这些:
- PRIMARY KEY:主键,唯一标识一条记录。每个表只能有一个。
- NOT NULL:字段不能为空。比如用户名,必须得有。
- UNIQUE:字段值必须唯一。比如邮箱、手机号。
- DEFAULT:默认值。插入数据时如果不指定,就用这个值。
- CHECK:检查条件。比如年龄必须大于0。
- FOREIGN KEY:外键,关联其他表。SQLite默认不开启外键约束,需要手动启用。
来看一个带完整约束的例子:
CREATE TABLE orders (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
product_name TEXT NOT NULL,
quantity INTEGER CHECK(quantity > 0),
price REAL CHECK(price >= 0),
status TEXT DEFAULT 'pending',
FOREIGN KEY (user_id) REFERENCES users(id)
);
-- 启用外键约束(每次连接都需要执行)
PRAGMA foreign_keys = ON;
这里我用了CHECK约束来限制数量和价格。曾经有个电商项目,因为没加CHECK约束,用户下了个数量为-1的订单,导致库存计算全乱了。从那以后,我但凡涉及数值字段,都会加上CHECK。
知识体系总览
下面这张图,是我梳理的SQLite基础知识点结构。你可以把它当作学习地图:
这张图把SQLite基础分成了6个模块。你从中心出发,沿着每个分支往下走,就能把知识点串起来。我个人建议的学习顺序是:先理解特点,再动手创建数据库和表,然后掌握数据类型和约束,最后在Qt中实践。
避坑指南
最后,分享几个我踩过的坑:
- 路径问题:数据库文件路径不要写死。用
QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)获取可写目录。 - 并发写入:SQLite支持多线程读,但写操作是串行的。高并发场景下,写入会排队。我曾经有个日志系统,每秒写几百条,结果卡得不行。后来改成批量写入,问题解决。
- 事务优化:批量插入数据时,一定要用事务包裹。不加事务的话,每条插入都会触发一次磁盘写入,速度慢10倍以上。
- VACUUM命令:SQLite删除数据后,文件大小不会自动缩小。定期执行
VACUUM可以回收空间。
好了,SQLite的基础知识就讲到这里。这些内容虽然基础,但都是我在实际项目中反复验证过的。你把这些吃透了,后面做Qt数据库集成就会顺手很多。
可可精品资料库