在开发一个电影评分相关的应用时,很多人会纠结该选哪种数据库。对于单机工具、小型网站或者学习用途来说,SQLite是一个非常务实的选择。它不需要单独启动服务进程,整个数据库就是一个文件,拷贝走就能用。本文以一个完整的电影评分数据库实战项目为线索,从表结构设计、数据写入到复杂查询,带你把核心能力走一遍。

数据库表结构设计与外键约束
电影评分系统的核心实体通常有三个:用户、电影、评分记录。用户表保存基本信息,电影表保存影片元数据,评分表则把前两者关联起来并记录分数。在SQLite中,虽然默认外键约束是关闭的,但我们可以在连接后执行PRAGMA foreign_keys = ON;来启用,从而防止出现指向不存在电影或用户的脏数据。
下面给出建表语句。注意评分表中的movie_id和user_id都通过REFERENCES指向对应主表,并设置了级联删除,这样当某部电影被删除时,相关评分也会自动清理,避免残留无效记录。
PRAGMA foreign_keys = ON;
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE movies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
director TEXT,
release_year INTEGER
);
CREATE TABLE ratings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
movie_id INTEGER NOT NULL,
score INTEGER NOT NULL CHECK (score >= 1 AND score <= 10),
rated_at TEXT DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (movie_id) REFERENCES movies(id) ON DELETE CASCADE
);
上面的CHECK约束保证分数只能在1到10之间,这是SQLite支持的表级约束能力。很多初学者以为SQLite只是轻量存储,其实它对约束、事务、视图的支持已经能满足绝大多数中小型项目。合理设计字段类型和约束,比后期写代码校验要可靠得多。
写入测试数据与事务批量插入
表建好之后,我们需要填充一些样例数据来验证查询逻辑。在真实项目中,评分往往是分散写入的,但演示时可以用事务一次性提交,既提升速度也保证原子性。SQLite的事务非常轻量,用BEGIN和COMMIT包裹多条INSERT即可。
以下代码先插入两名用户和三部电影,再模拟他们各自的评分。通过事务包裹,如果中间某条语句失败,前面插入的内容也不会落盘,保持数据库干净。
BEGIN;
INSERT INTO users (username) VALUES ('alice'), ('bob');
INSERT INTO movies (title, director, release_year) VALUES
('星际穿越', '诺兰', 2014),
('千与千寻', '宫崎骏', 2001),
('盗梦空间', '诺兰', 2010);
INSERT INTO ratings (user_id, movie_id, score) VALUES
(1, 1, 9),
(1, 2, 8),
(2, 1, 10),
(2, 3, 7),
(1, 3, 9);
COMMIT;
插入完成后,可以执行简单查询确认数据完整性。比如SELECT * FROM ratings;能看到五条评分。这里要提醒一点,SQLite的AUTOINCREMENT会占用额外空间并记录最大ID,如果项目不需要严格递增,用INTEGER PRIMARY KEY即可,插入性能还会更好。实战中应根据业务取舍。
联表查询与评分统计实战
电影评分数据库最有价值的部分就是统计。我们常需要计算每部电影的平均分、评分人数,以及某个用户看过的所有电影及打分。这就要用到JOIN和聚合函数。SQLite完整支持INNER JOIN、LEFT JOIN以及GROUP BY。
下面这条语句联表求出每部电影的平均分与评分次数,并按平均分降序排列。通过ROUND函数把平均分包两位,方便展示。
SELECT
m.title,
COUNT(r.id) AS rating_count,
ROUND(AVG(r.score), 2) AS avg_score
FROM movies m
LEFT JOIN ratings r ON m.id = r.movie_id
GROUP BY m.id
ORDER BY avg_score DESC;
如果我们想看用户alice评价过的影片清单,可以把用户表和评分表、电影表三层连接。这种写法在报表类需求里极其常见,也是检验你是否掌握关系模型的关键。
SELECT
u.username,
m.title,
r.score,
r.rated_at
FROM users u
JOIN ratings r ON u.id = r.user_id
JOIN movies m ON r.movie_id = m.id
WHERE u.username = 'alice'
ORDER BY r.score DESC;
当数据量增长到几万条评分时,建议在ratings.movie_id上建索引,否则GROUP BY movie_id会全表扫描。可以用CREATE INDEX idx_ratings_movie ON ratings(movie_id);解决。SQLite的查询规划器会自动选用索引,不需要改SQL语句。掌握这些查询与优化手段,你的电影评分数据库就能从玩具变成真正可用的小系统。